diff --git a/.cspell.json b/.cspell.json index c7abe9c..74a443a 100644 --- a/.cspell.json +++ b/.cspell.json @@ -14,6 +14,7 @@ "mypy", "optvar", "pdoc", + "pyright", "setuptools", "startswith", "tomli" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c15b27f..0dfd5f4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,28 +10,23 @@ jobs: strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Set up caches - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-py${{ matrix.python-version }} - - name: Checkout repo - uses: actions/checkout@v2 - with: - fetch-depth: 3 - - - name: Fetch tags - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - - name: Install dependencies run: | python -m pip install --upgrade pip setuptools wheel @@ -49,6 +44,10 @@ jobs: run: | pdm run mypy + # - name: Type check (pyright) + # run: | + # pdm run pyright + - name: Run tests run: | pdm run test diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e36c199..92dab7f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,24 +1,11 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# name: "CodeQL" on: push: - branches: [ main ] + branches: [main] pull_request: # The branches below must be a subset of the branches above - branches: [ main ] - # schedule: - # - cron: '35 7 * * 3' + branches: [main] jobs: analyze: @@ -32,51 +19,22 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'python' ] + language: ["python"] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - - name: Checkout repository - uses: actions/checkout@v2 + - name: Checkout repository + uses: actions/checkout@v4 - - name: Set up python - uses: actions/setup-python@v2 - with: - python-version: '3.x' + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - echo "CODEQL_PYTHON=$(which python)" >> $GITHUB_ENV + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - setup-python-dependencies: false - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/pypi.yaml b/.github/workflows/pypi.yaml index 069729f..5f6310c 100644 --- a/.github/workflows/pypi.yaml +++ b/.github/workflows/pypi.yaml @@ -5,34 +5,33 @@ on: types: [published] jobs: - deploy: - + pypi-publish: runs-on: ubuntu-latest + permissions: + id-token: write + steps: - - name: Checkout repo - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build - - - name: Check setup.py - run: | - pip install -e . - - - name: Build package - run: | - python -m build - - - name: Publish package - uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.x" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools wheel + pip install build + + - name: Check install + run: | + pip install -e . + + - name: Build package + run: | + python -m build + + - name: Publish package + uses: pypa/gh-action-pypi-publish@2f6f737ca5f74c637829c0f5c3acd0e29ea5e8bf diff --git a/.gitignore b/.gitignore index e3ca062..0b3e0b6 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,7 @@ _seed.py __pycache__ # test -.coverage* +.coverage .mypy_cache .pytest_cache .ruff_cache diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..77c71b8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,89 @@ +# Contributing + +## Local Development + +```bash +# get the code +git clone git@github.com:metaist/attrbox.git +cd attrbox + +# create a virtual environment +python -m venv .venv --prompt attrbox +. .venv/bin/activate +pip install --upgrade pip + +# install dependencies and dev tools +pip install -e ".[dev]" +pnpm install -g cspell +``` + +As you work on the code, you should periodically run: + +```bash +pdm lint # for type checks +pdm test # for unit tests +``` + +This repo generally tries to maintain type-correctness (via `mypy` and `pyright`) and complete unit test coverage. + +## Making a Release + +Checkout `prod`: + +```bash +git checkout prod +git merge --no-ff --no-edit main +``` + +Update top-most `__init__.py`: + +```python +__version__ = "X.0.1" +``` + +Update `CHANGELOG.md`: + +Sections order is: `Fixed`, `Changed`, `Added`, `Deprecated`, `Removed`, `Security`. + +```markdown +--- + +[X.0.1]: https://github.com/metaist/attrbox/compare/X.0.0...X.0.1 + +## [X.0.1] - XXXX-XX-XXT00:00:00Z + +**Fixed** + +**Changed** + +**Added** + +**Deprecated** + +**Removed** + +**Security** +``` + +### + +```bash +export VER="X.0.1" + +# update docs +pdm docs + +# check build +pip install -e . + +# commit and push tags +git commit -am "release: $VER" +git tag $VER +git push +git push --tags +git checkout main +git merge --no-ff --no-edit prod +git push +``` + +[Create the release on GitHub](https://github.com/metaist/attrbox/releases/new). The `pypi.yaml` workflow will attempt to publish it to PyPI. diff --git a/README.md b/README.md index 328c6ad..20e9bfa 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,14 @@ -# attrbox - -_Attribute-based data structures._ - -[![Build Status](https://img.shields.io/github/actions/workflow/status/metaist/attrbox/.github/workflows/ci.yaml?branch=main&style=for-the-badge)](https://github.com/metaist/attrbox/actions) -[![attrbox on PyPI](https://img.shields.io/pypi/v/attrbox.svg?color=blue&style=for-the-badge)](https://pypi.org/project/attrbox) -[![Supported Python versions](https://img.shields.io/pypi/pyversions/attrbox?style=for-the-badge)](https://pypi.org/project/attrbox) - -[Changelog] - [Issues] - [Documentation] - -[changelog]: https://github.com/metaist/attrbox/blob/main/CHANGELOG.md -[issues]: https://github.com/metaist/attrbox/issues -[documentation]: https://metaist.github.io/attrbox/ +# attrbox: attribute-based data structures + +
+ ## Why? diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/attrdict.html b/docs/attrdict.html index c176520..614ddff 100644 --- a/docs/attrdict.html +++ b/docs/attrdict.html @@ -80,16 +80,16 @@attrbox.attrdict
attrbox.attrdict
attrbox.attrdict
attrbox.attrdict
attrbox.attrdict
attrbox.attrdict
-def __contains__(self, name: Any) ‑> bool
+def __contains__(self, key: Any) ‑> bool
Return True
if name
is a key.
Return True
if key
is a key.
name
: Any
key
: Any
AnyIndex
. If it's a string,
the check proceeds as usual. If it's a Sequence
, the
checks are performed using .get()
.bool
True
if the name
is a valid key, False
otherwise.True
if the key
is a valid key, False
otherwise.Normal checking works as expected:
@@ -793,16 +793,16 @@def __contains__(self, name: Any) -> bool:
- """Return `True` if `name` is a key.
+def __contains__(self, key: Any) -> bool:
+ """Return `True` if `key` is a key.
Args:
- name (Any): typically a `AnyIndex`. If it's a string,
+ key (Any): typically a `AnyIndex`. If it's a string,
the check proceeds as usual. If it's a `Sequence`, the
checks are performed using `.get()`.
Returns:
- bool: `True` if the `name` is a valid key, `False` otherwise.
+ bool: `True` if the `key` is a valid key, `False` otherwise.
Examples:
Normal checking works as expected:
@@ -819,7 +819,7 @@ Examples
>>> ['a', 1, 'x'] in items
False
"""
- return self.get(name, NOT_FOUND) is not NOT_FOUND
+ return self.get(key, NOT_FOUND) is not NOT_FOUND
@@ -1034,13 +1034,13 @@ Examples
-def __getitem__(self, name: AnyIndex) ‑> Optional[Any]
+def __getitem__(self, key: AnyIndex) ‑> Optional[Any]
-
Return the value of the key.
Args
-name
: AnyIndex
+key
: AnyIndex
- key name or Sequence of the path to a key
Returns
@@ -1059,11 +1059,11 @@ Examples
Expand source code
-def __getitem__(self, name: AnyIndex) -> Optional[Any]:
+def __getitem__(self, key: AnyIndex) -> Optional[Any]:
"""Return the value of the key.
Args:
- name (AnyIndex): key name or Sequence of the path to a key
+ key (AnyIndex): key name or Sequence of the path to a key
Returns:
Any: value of the key or `None` if it cannot be found
@@ -1075,17 +1075,17 @@ Examples
>>> item['b'] is None
True
"""
- return self.get(name)
+ return self.get(key)
-def __setitem__(self, name: AnyIndex, value: Any) ‑> None
+def __setitem__(self, key: AnyIndex, value: Any) ‑> None
-
Set the value of a key.
Args
-name
: BoxIndex
+key
: AnyIndex
- key name
value
: Any
- key value
@@ -1104,11 +1104,11 @@ Examples
Expand source code
-def __setitem__(self, name: AnyIndex, value: Any) -> None:
+def __setitem__(self, key: AnyIndex, value: Any) -> None:
"""Set the value of a key.
Args:
- name (BoxIndex): key name
+ key (AnyIndex): key name
value (Any): key value
Examples:
@@ -1121,17 +1121,17 @@ Examples
>>> item.a.b
10
"""
- self.set(name, value)
+ self.set(key, value)
-def __delitem__(self, name: str) ‑> None
+def __delitem__(self, key: str) ‑> None
-
Delete a key.
Args
-name
: str
+key
: str
- key name
Examples
@@ -1150,11 +1150,11 @@ Examples
Expand source code
-def __delitem__(self, name: str) -> None:
+def __delitem__(self, key: str) -> None:
"""Delete a key.
Args:
- name (str): key name
+ key (str): key name
Examples:
>>> item = AttrDict(a=1)
@@ -1169,7 +1169,7 @@ Examples
{'a': 1}
"""
try:
- super().__delitem__(name)
+ super().__delitem__(key)
except KeyError:
pass
diff --git a/docs/config.html b/docs/config.html
index 2a41d43..5dacc4b 100644
--- a/docs/config.html
+++ b/docs/config.html
@@ -36,6 +36,7 @@ Module attrbox.config
from typing import Callable
from typing import Dict
from typing import List
+from typing import LiteralString
from typing import Mapping
from typing import Optional
from typing import Sequence
@@ -51,7 +52,7 @@ Module attrbox.config
from . import env
PYTHON_KEYWORDS: List[
- str
+ LiteralString
] = """\
False await else import pass
None break except in raise
@@ -300,7 +301,7 @@ Module attrbox.config
Global variables
-var PYTHON_KEYWORDS : List[str]
+var PYTHON_KEYWORDS : List[LiteralString]
-
diff --git a/docs/env.html b/docs/env.html
index f45477a..00a0420 100644
--- a/docs/env.html
+++ b/docs/env.html
@@ -113,6 +113,7 @@ Examples
def read(self) -> str:
"""Read the contents of the file-like object."""
+ return "" # pragma: no cover
def expand(
@@ -175,7 +176,7 @@ Examples
name = name.split(".")
if name in values:
- value = str(values[name])
+ value = str(values[name]) # pyright: ignore
return value
return _RE_EXPAND.sub(_repl, value)
@@ -478,7 +479,7 @@ Examples
name = name.split(".")
if name in values:
- value = str(values[name])
+ value = str(values[name]) # pyright: ignore
return value
return _RE_EXPAND.sub(_repl, value)
@@ -658,7 +659,7 @@ Examples
-def find_env(path: Union[pathlib.Path, str, None] = None, name: str = '.env') ‑> Optional[pathlib.Path]
+def find_env(path: Union[pathlib.Path, str, ForwardRef(None)] = None, name: str = '.env') ‑> Optional[pathlib.Path]
-
Find the .env
file in the ancestors of the current path.
@@ -740,7 +741,7 @@ Examples
-def load_env(path: Union[pathlib.Path, str, None] = None) ‑> Dict[str, str]
+def load_env(path: Union[pathlib.Path, str, ForwardRef(None)] = None) ‑> Dict[str, str]
-
Load an environment file.
@@ -827,7 +828,8 @@ Classes
"""Protocol for a class that implements a `.read()` method."""
def read(self) -> str:
- """Read the contents of the file-like object."""
+ """Read the contents of the file-like object."""
+ return "" # pragma: no cover
Ancestors
@@ -846,7 +848,8 @@ Methods
Expand source code
def read(self) -> str:
- """Read the contents of the file-like object."""
+ """Read the contents of the file-like object."""
+ return "" # pragma: no cover
diff --git a/docs/fn.html b/docs/fn.html
index 3d5473c..5eb635f 100644
--- a/docs/fn.html
+++ b/docs/fn.html
@@ -64,6 +64,7 @@ Module attrbox.fn
def __contains__(self, key: Any) -> bool:
"""Return `True` if `key` exists, `False` otherwise."""
+ return False
def __getitem__(self, key: Any) -> Any:
"""Return value of `key`."""
@@ -516,11 +517,13 @@ Classes
Expand source code
-class SupportsItem(Protocol): # pragma: no cover
+@runtime_checkable
+class SupportsItem(Protocol): # pragma: no cover
"""Protocol for `k in x`, `x[k]`, and `x[k] = v`."""
def __contains__(self, key: Any) -> bool:
"""Return `True` if `key` exists, `False` otherwise."""
+ return False
def __getitem__(self, key: Any) -> Any:
"""Return value of `key`."""
diff --git a/docs/index.html b/docs/index.html
index 889f6e7..7d2a563 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -23,10 +23,15 @@ Package attrbox
Attribute-based data structures.
-
-Changelog - Issues - Documentation
+
+
Why?
I have common use cases where I want to improve python's dict
and list
:
@@ -147,7 +152,7 @@ License
"""Attribute-based data structures.
.. include:: ../../README.md
- :start-line: 4
+ :start-line: 2
"""
# pkg
@@ -215,7 +220,7 @@ Sub-modules
Functions
-def load_env(path: Union[pathlib.Path, str, None] = None) ‑> Dict[str, str]
+def load_env(path: Union[pathlib.Path, str, ForwardRef(None)] = None) ‑> Dict[str, str]
-
Load an environment file.
@@ -533,16 +538,16 @@ Examples
"""
return self.__class__(super().copy())
- def __contains__(self, name: Any) -> bool:
- """Return `True` if `name` is a key.
+ def __contains__(self, key: Any) -> bool:
+ """Return `True` if `key` is a key.
Args:
- name (Any): typically a `AnyIndex`. If it's a string,
+ key (Any): typically a `AnyIndex`. If it's a string,
the check proceeds as usual. If it's a `Sequence`, the
checks are performed using `.get()`.
Returns:
- bool: `True` if the `name` is a valid key, `False` otherwise.
+ bool: `True` if the `key` is a valid key, `False` otherwise.
Examples:
Normal checking works as expected:
@@ -559,7 +564,7 @@ Examples
>>> ['a', 1, 'x'] in items
False
"""
- return self.get(name, NOT_FOUND) is not NOT_FOUND
+ return self.get(key, NOT_FOUND) is not NOT_FOUND
def __getattr__(self, name: str) -> Optional[Any]:
"""Return the value of the attribute or `None`.
@@ -659,11 +664,11 @@ Examples
except AttributeError: # use key/value
del self[name]
- def __getitem__(self, name: AnyIndex) -> Optional[Any]:
+ def __getitem__(self, key: AnyIndex) -> Optional[Any]:
"""Return the value of the key.
Args:
- name (AnyIndex): key name or Sequence of the path to a key
+ key (AnyIndex): key name or Sequence of the path to a key
Returns:
Any: value of the key or `None` if it cannot be found
@@ -675,13 +680,13 @@ Examples
>>> item['b'] is None
True
"""
- return self.get(name)
+ return self.get(key)
- def __setitem__(self, name: AnyIndex, value: Any) -> None:
+ def __setitem__(self, key: AnyIndex, value: Any) -> None:
"""Set the value of a key.
Args:
- name (BoxIndex): key name
+ key (AnyIndex): key name
value (Any): key value
Examples:
@@ -694,13 +699,13 @@ Examples
>>> item.a.b
10
"""
- self.set(name, value)
+ self.set(key, value)
- def __delitem__(self, name: str) -> None:
+ def __delitem__(self, key: str) -> None:
"""Delete a key.
Args:
- name (str): key name
+ key (str): key name
Examples:
>>> item = AttrDict(a=1)
@@ -715,7 +720,7 @@ Examples
{'a': 1}
"""
try:
- super().__delitem__(name)
+ super().__delitem__(key)
except KeyError:
pass
@@ -864,13 +869,13 @@ Examples
-def __contains__(self, name: Any) ‑> bool
+def __contains__(self, key: Any) ‑> bool
-
-
Return True
if name
is a key.
+Return True
if key
is a key.
Args
-name
: Any
+key
: Any
- typically a
AnyIndex
. If it's a string,
the check proceeds as usual. If it's a Sequence
, the
checks are performed using .get()
.
@@ -878,7 +883,7 @@ Args
Returns
bool
-True
if the name
is a valid key, False
otherwise.
+True
if the key
is a valid key, False
otherwise.
Examples
Normal checking works as expected:
@@ -899,16 +904,16 @@ Examples
Expand source code
-def __contains__(self, name: Any) -> bool:
- """Return `True` if `name` is a key.
+def __contains__(self, key: Any) -> bool:
+ """Return `True` if `key` is a key.
Args:
- name (Any): typically a `AnyIndex`. If it's a string,
+ key (Any): typically a `AnyIndex`. If it's a string,
the check proceeds as usual. If it's a `Sequence`, the
checks are performed using `.get()`.
Returns:
- bool: `True` if the `name` is a valid key, `False` otherwise.
+ bool: `True` if the `key` is a valid key, `False` otherwise.
Examples:
Normal checking works as expected:
@@ -925,7 +930,7 @@ Examples
>>> ['a', 1, 'x'] in items
False
"""
- return self.get(name, NOT_FOUND) is not NOT_FOUND
+ return self.get(key, NOT_FOUND) is not NOT_FOUND
@@ -1140,13 +1145,13 @@ Examples
-def __getitem__(self, name: AnyIndex) ‑> Optional[Any]
+def __getitem__(self, key: AnyIndex) ‑> Optional[Any]
-
Return the value of the key.
Args
-name
: AnyIndex
+key
: AnyIndex
- key name or Sequence of the path to a key
Returns
@@ -1165,11 +1170,11 @@ Examples
Expand source code
-def __getitem__(self, name: AnyIndex) -> Optional[Any]:
+def __getitem__(self, key: AnyIndex) -> Optional[Any]:
"""Return the value of the key.
Args:
- name (AnyIndex): key name or Sequence of the path to a key
+ key (AnyIndex): key name or Sequence of the path to a key
Returns:
Any: value of the key or `None` if it cannot be found
@@ -1181,17 +1186,17 @@ Examples
>>> item['b'] is None
True
"""
- return self.get(name)
+ return self.get(key)
-def __setitem__(self, name: AnyIndex, value: Any) ‑> None
+def __setitem__(self, key: AnyIndex, value: Any) ‑> None
-
Set the value of a key.
Args
-name
: BoxIndex
+key
: AnyIndex
- key name
value
: Any
- key value
@@ -1210,11 +1215,11 @@ Examples
Expand source code
-def __setitem__(self, name: AnyIndex, value: Any) -> None:
+def __setitem__(self, key: AnyIndex, value: Any) -> None:
"""Set the value of a key.
Args:
- name (BoxIndex): key name
+ key (AnyIndex): key name
value (Any): key value
Examples:
@@ -1227,17 +1232,17 @@ Examples
>>> item.a.b
10
"""
- self.set(name, value)
+ self.set(key, value)
-def __delitem__(self, name: str) ‑> None
+def __delitem__(self, key: str) ‑> None
-
Delete a key.
Args
-name
: str
+key
: str
- key name
Examples
@@ -1256,11 +1261,11 @@ Examples
Expand source code
-def __delitem__(self, name: str) -> None:
+def __delitem__(self, key: str) -> None:
"""Delete a key.
Args:
- name (str): key name
+ key (str): key name
Examples:
>>> item = AttrDict(a=1)
@@ -1275,7 +1280,7 @@ Examples
{'a': 1}
"""
try:
- super().__delitem__(name)
+ super().__delitem__(key)
except KeyError:
pass
@@ -2568,14 +2573,14 @@ Examples
Return a shallow copy …
-def __contains__(self, name: Any) ‑> bool
+def __contains__(self, key: Any) ‑> bool
-
Inherited from:
AttrDict
.__contains__
-Return True
if name
is a key …
+Return True
if key
is a key …
def __getattr__(self, name: str) ‑> Optional[Any]
@@ -2608,7 +2613,7 @@ Examples
Delete an attribute …
-def __getitem__(self, name: AnyIndex) ‑> Optional[Any]
+def __getitem__(self, key: AnyIndex) ‑> Optional[Any]
-
@@ -2618,7 +2623,7 @@
Examples
Return the value of the key …
-def __setitem__(self, name: AnyIndex, value: Any) ‑> None
+def __setitem__(self, key: AnyIndex, value: Any) ‑> None
-
@@ -2628,7 +2633,7 @@
Examples
Set the value of a key …
-def __delitem__(self, name: str) ‑> None
+def __delitem__(self, key: str) ‑> None
-
diff --git a/docs/jsend.html b/docs/jsend.html
index dc5693b..49a73b5 100644
--- a/docs/jsend.html
+++ b/docs/jsend.html
@@ -460,14 +460,14 @@
Examples
Return a shallow copy …
-def __contains__(self, name: Any) ‑> bool
+def __contains__(self, key: Any) ‑> bool
-
Inherited from:
AttrDict
.__contains__
-Return True
if name
is a key …
+Return True
if key
is a key …
def __getattr__(self, name: str) ‑> Optional[Any]
@@ -500,7 +500,7 @@ Examples
Delete an attribute …
-def __getitem__(self, name: AnyIndex) ‑> Optional[Any]
+def __getitem__(self, key: AnyIndex) ‑> Optional[Any]
-
@@ -510,7 +510,7 @@
Examples
Return the value of the key …
-def __setitem__(self, name: AnyIndex, value: Any) ‑> None
+def __setitem__(self, key: AnyIndex, value: Any) ‑> None
-
@@ -520,7 +520,7 @@
Examples
Set the value of a key …
-def __delitem__(self, name: str) ‑> None
+def __delitem__(self, key: str) ‑> None
-
diff --git a/otter-box.png b/otter-box.png
new file mode 100644
index 0000000..d275c40
Binary files /dev/null and b/otter-box.png differ
diff --git a/pyproject.toml b/pyproject.toml
index 4d9cc5c..2dbc362 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -36,6 +36,7 @@ optional-dependencies = { dev = [
"mypy",
"pdm",
"pdoc3",
+ "pyright",
"pytest-cov",
"pytest",
"ruff",
@@ -56,6 +57,7 @@ classifiers = [
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Libraries",
"Typing :: Typed",
]
@@ -64,6 +66,7 @@ classifiers = [
black = { shell = "black ." }
ruff = { shell = "ruff ." }
cspell = { shell = "cspell --gitignore '**/*.{py,txt,md,markdown}'" }
+pyright = { shell = "pyright src test" }
mypy = { shell = """\
mypy \
@@ -92,8 +95,9 @@ docs = { shell = """\
--config show_inherited_members=True \
--force src/$(basename $(pwd));
mv docs/**/* docs/
+ touch docs/.nojekyll
""" }
-lint = { composite = ["black", "ruff", "cspell", "mypy"] }
+lint = { composite = ["black", "ruff", "cspell", "pyright", "mypy"] }
dev = { composite = ["lint", "test"] }
all = { composite = ["lint", "test", "docs"] }
diff --git a/src/attrbox/__init__.py b/src/attrbox/__init__.py
index afb4ce2..9205f20 100644
--- a/src/attrbox/__init__.py
+++ b/src/attrbox/__init__.py
@@ -1,7 +1,7 @@
"""Attribute-based data structures.
.. include:: ../../README.md
- :start-line: 4
+ :start-line: 2
"""
# pkg