Skip to content

Commit

Permalink
Feat: Add Python stub
Browse files Browse the repository at this point in the history
  • Loading branch information
RF-Tar-Railt authored and amitdev committed Nov 5, 2023
1 parent e68d991 commit ba03600
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 40 deletions.
133 changes: 132 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,132 @@
.DS_Store
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pdm config
.pdm-python
3 changes: 3 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
include LICENSE README.rst MANIFEST MANIFEST.in
graft src
global-exclude *.pyc
global-exclude *.cache
39 changes: 39 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[project]
name = "lru-dict"
version = "1.2.0"
description = "An Dict like LRU container."
authors = [
{name = "Amit Dev"},
]
dependencies = []
requires-python = ">=3.7"
readme = "README.rst"
license = {text = "MIT"}
keywords = ["lru", "dict"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Operating System :: POSIX",
"Programming Language :: C",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
"Topic :: Software Development :: Libraries :: Python Modules",
]
[project.urls]
Homepage = "https://github.com/amitdev/lru-dict"
[project.optional-dependencies]
test = [
"pytest",
]

[build-system]
requires = ["setuptools>=61", "wheel"]
build-backend = "setuptools.build_meta"
41 changes: 9 additions & 32 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,12 @@
from setuptools import setup, Extension

module1 = Extension('lru',
sources = ['lru.c'])
extensions = [
Extension("lru._lru", ["src/lru/_lru.c"]),
]

setup (name = 'lru-dict',
version = '1.2.0',
description = 'An Dict like LRU container.',
long_description = open('README.rst').read(),
long_description_content_type="text/x-rst",
author='Amit Dev',
url='https://github.com/amitdev/lru-dict',
license='MIT',
keywords='lru, dict',
ext_modules = [module1],
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Operating System :: POSIX',
'Programming Language :: C',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: Implementation :: CPython',
'Topic :: Software Development :: Libraries :: Python Modules',
],
extras_require={
'test': ['pytest'],
},
)
args = {
"include_package_data": True,
"exclude_package_data": {"": ["*.c"]},
}

setup(ext_modules=extensions, **args)
3 changes: 3 additions & 0 deletions src/lru/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from ._lru import LRU as LRU # noqa: F401

__all__ = ["LRU"]
63 changes: 63 additions & 0 deletions src/lru/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from typing import (
Any,
Callable,
Generic,
Hashable,
Iterable,
TypeVar,
overload,
Protocol
)

_KT = TypeVar("_KT", bound=Hashable)
_VT = TypeVar("_VT")
_VT_co = TypeVar("_VT_co", covariant=True)
_T = TypeVar("_T")


class __SupportsKeysAndGetItem(Protocol[_KT, _VT_co]):
def keys(self) -> Iterable[_KT]: ...
def __getitem__(self, __key: _KT) -> _VT_co: ...


class LRU(Generic[_KT, _VT]):
@overload
def __init__(self, size: int) -> None: ...
@overload
def __init__(self, size: int, callback: Callable[[_KT, _VT], Any]) -> None: ...
def clear(self) -> None: ...
@overload
def get(self, key: _KT) -> _VT | None: ...
@overload
def get(self, key: _KT, instead: _VT | _T) -> _VT | _T: ...
def get_size(self) -> int: ...
def has_key(self, key: _KT) -> bool: ...
def keys(self) -> list[_KT]: ...
def values(self) -> list[_VT]: ...
def items(self) -> list[tuple[_KT, _VT]]: ...
def peek_first_item(self) -> tuple[_KT, _VT] | None: ...
def peek_last_item(self) -> tuple[_KT, _VT] | None: ...
@overload
def pop(self, key: _KT) -> _VT | None: ...
@overload
def pop(self, key: _KT, default: _VT | _T) -> _VT | _T: ...
def popitem(self, least_recent: bool = ...) -> tuple[_KT, _VT]: ...
@overload
def setdefault(self: LRU[_KT, _T | None], key: _KT) -> _T | None: ...
@overload
def setdefault(self, key: _KT, default: _VT) -> _VT: ...
def set_callback(self, callback: Callable[[_KT, _VT], Any] | None) -> None: ...
def set_size(self, size: int) -> None: ...
@overload
def update(self, __m: __SupportsKeysAndGetItem[_KT, _VT], **kwargs: _VT) -> None: ...
@overload
def update(self, __m: Iterable[tuple[_KT, _VT]], **kwargs: _VT) -> None: ...
@overload
def update(self, **kwargs: _VT) -> None: ...
def get_stats(self) -> tuple[int, int]: ...
def __contains__(self, __o: Any) -> bool: ...
def __delitem__(self, key: _KT) -> None: ...
def __getitem__(self, item: _KT) -> _VT: ...
def __len__(self) -> int: ...
def __repr__(self) -> str: ...
def __setitem__(self, key: _KT, value: _VT) -> None: ...
8 changes: 4 additions & 4 deletions lru.c → src/lru/_lru.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ node_repr(Node* self)

static PyTypeObject NodeType = {
PyVarObject_HEAD_INIT(NULL, 0)
"lru.Node", /* tp_name */
"_lru.Node", /* tp_name */
sizeof(Node), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)node_dealloc,/* tp_dealloc */
Expand Down Expand Up @@ -711,7 +711,7 @@ PyDoc_STRVAR(lru_doc,

static PyTypeObject LRUType = {
PyVarObject_HEAD_INIT(NULL, 0)
"lru.LRU", /* tp_name */
"_lru.LRU", /* tp_name */
sizeof(LRU), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)LRU_dealloc, /* tp_dealloc */
Expand Down Expand Up @@ -753,7 +753,7 @@ static PyTypeObject LRUType = {
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"lru", /* m_name */
"_lru", /* m_name */
lru_doc, /* m_doc */
-1, /* m_size */
NULL, /* m_methods */
Expand All @@ -780,7 +780,7 @@ moduleinit(void)
#if PY_MAJOR_VERSION >= 3
m = PyModule_Create(&moduledef);
#else
m = Py_InitModule3("lru", NULL, lru_doc);
m = Py_InitModule3("_lru", NULL, lru_doc);
#endif

if (m == NULL)
Expand Down
6 changes: 3 additions & 3 deletions test/test_lru.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,11 @@ def test_pop(self):
self.assertEqual(0, len(l))
with self.assertRaises(KeyError) as ke:
l.pop(4)
self.assertEqual(4, ke.args[0])
self.assertEqual(4, ke.args[0]) # type: ignore
self.assertEqual((2, 2), l.get_stats())
self.assertEqual(0, len(l))
with self.assertRaises(TypeError):
l.pop()
l.pop() # type: ignore

def test_popitem(self):
l = LRU(3)
Expand All @@ -265,7 +265,7 @@ def test_popitem(self):
self.assertEqual((2, '2'), l.popitem(True))
with self.assertRaises(KeyError) as ke:
l.popitem()
self.assertEqual('popitem(): LRU dict is empty', ke.args[0])
self.assertEqual('popitem(): LRU dict is empty', ke.args[0]) # type: ignore
self.assertEqual((0, 0), l.get_stats())

def test_stats(self):
Expand Down

0 comments on commit ba03600

Please sign in to comment.