From 6fccc3426e81bcb0c6cdb789a75775248812d4fb Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Mon, 3 Apr 2023 19:13:22 -0500 Subject: [PATCH 01/13] * Removing #251 support for circular references in lists --- CHANGES.rst | 6 ++++++ box/box_list.py | 3 +-- test/test_box.py | 9 --------- test/test_box_list.py | 11 +++++++++++ 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index eb7921b..eb5da5c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,12 @@ Changelog ========= +Version 7.1.0 +------------- + +* Removing #251 support for circular references in lists + + Version 7.0.1 ------------- diff --git a/box/box_list.py b/box/box_list.py index 750f3f0..b51f2fc 100644 --- a/box/box_list.py +++ b/box/box_list.py @@ -22,7 +22,6 @@ _to_yaml, msgpack_available, toml_read_library, - toml_write_library, yaml_available, ) from box.exceptions import BoxError, BoxTypeError @@ -102,7 +101,7 @@ def _convert(self, p_object): elif isinstance(p_object, box.Box): p_object._box_config.update(self.box_options) if isinstance(p_object, list) and not self._is_intact_type(p_object): - p_object = self if id(p_object) == self.box_org_ref else self.__class__(p_object, **self.box_options) + p_object = self.__class__(p_object, **self.box_options) elif isinstance(p_object, BoxList): p_object.box_options.update(self.box_options) return p_object diff --git a/test/test_box.py b/test/test_box.py index c7b51b7..50b5398 100644 --- a/test/test_box.py +++ b/test/test_box.py @@ -588,15 +588,6 @@ def test_circular_references(self): bx.to_json() - circular_list = [] - circular_list.append(circular_list) - bl = BoxList(circular_list) - assert bl == bl[0] - assert isinstance(bl[0], BoxList) - circular_list_2 = bl.to_list() - assert circular_list_2 == circular_list_2[0] - assert isinstance(circular_list_2, list) - def test_to_multiline(self): a = BoxList([Box(a=1), Box(b=2), Box(three=5)]) diff --git a/test/test_box_list.py b/test/test_box_list.py index e68e521..0fd05d3 100644 --- a/test/test_box_list.py +++ b/test/test_box_list.py @@ -220,3 +220,14 @@ def test_box_config_propagate(self): assert item._box_config["default_box"] is True elif isinstance(item, BoxList): assert item.box_options["default_box"] is True + + def test_no_recursion_errors(self): + a = Box({"list_of_dicts": [[{"example1": 1}]]}) + a.list_of_dicts.append([{"example2": 2}]) + assert a['list_of_dicts'][1] == [{"example2": 2}] + + def test_no_circular_references(self): + circular_list = [] + circular_list.append(circular_list) + with pytest.raises(RecursionError): + BoxList(circular_list) From fe9637669a68a700284230712f91288830cc537f Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Wed, 14 Jun 2023 21:05:14 -0500 Subject: [PATCH 02/13] * Fixing #253 merge_update box list merge types not populated to sub dictionaries (thanks to lei wang) --- CHANGES.rst | 2 +- box/box.py | 2 +- test/test_box.py | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index eb5da5c..813368b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,9 +4,9 @@ Changelog Version 7.1.0 ------------- +* Fixing #253 merge_update box list merge types not populated to sub dictionaries (thanks to lei wang) * Removing #251 support for circular references in lists - Version 7.0.1 ------------- diff --git a/box/box.py b/box/box.py index 322b313..84599df 100644 --- a/box/box.py +++ b/box/box.py @@ -823,7 +823,7 @@ def convert_and_set(k, v): # in the `converted` box_config set v = self._box_config["box_class"](v, **self.__box_config(extra_namespace=k)) if k in self and isinstance(self[k], dict): - self[k].merge_update(v) + self[k].merge_update(v, box_merge_lists=merge_type) return if isinstance(v, list) and not intact_type: v = box.BoxList(v, **self.__box_config(extra_namespace=k)) diff --git a/test/test_box.py b/test/test_box.py index 50b5398..3fd1ab0 100644 --- a/test/test_box.py +++ b/test/test_box.py @@ -1255,6 +1255,19 @@ def test_merge_list_options(self): a.merge_update({"key1": {"new": 5}, "Key 2": {"add_key": 6}, "lister": ["a"]}) assert a.lister == ["a"] + d1 = {'app': {'S3': {'S3Service': [{'bucket': 'bucket001'}]}}} + + d2 = {'app': {'S3': {'S3Service': [{'expirationDate': '2099-10-25'}]}}} + + box1 = Box(d1) + + box1.merge_update(d2, box_merge_lists='extend') + + assert box1 == Box({'app': {'S3': {'S3Service': [{'bucket': 'bucket001'}, {'expirationDate': '2099-10-25'}]}}}), box1 + + + + def test_box_from_empty_yaml(self): out = Box.from_yaml("---") assert out == Box() From e82a4161ea9b0870366df26444486766338ed8ee Mon Sep 17 00:00:00 2001 From: Eric Prestat Date: Thu, 15 Jun 2023 03:17:04 +0100 Subject: [PATCH 03/13] Defer ipython import (#255) --- box/box.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/box/box.py b/box/box.py index 84599df..354c7a7 100644 --- a/box/box.py +++ b/box/box.py @@ -18,12 +18,6 @@ except ImportError: from collections.abc import Callable, Iterable, Mapping -try: - from IPython import get_ipython -except ImportError: - ipython = False -else: - ipython = True if get_ipython() else False import box from box.converters import ( @@ -56,6 +50,17 @@ NO_NAMESPACE = object() +def _is_ipython(): + try: + from IPython import get_ipython + except ImportError: + ipython = False + else: + ipython = True if get_ipython() else False + + return ipython + + def _exception_cause(e): """ Unwrap BoxKeyError and BoxValueError errors to their cause. @@ -483,7 +488,7 @@ def __setstate__(self, state): self.__dict__.update(state) def __get_default(self, item, attr=False): - if ipython and item in ("getdoc", "shape"): + if item in ("getdoc", "shape") and _is_ipython(): return None default_value = self._box_config["default_box_attr"] if default_value in (self._box_config["box_class"], dict): From 1025c0ae23a3be2847029e1ebd29b42f7d32f73a Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Wed, 14 Jun 2023 21:24:42 -0500 Subject: [PATCH 04/13] Adding authors Version bump --- AUTHORS.rst | 4 ++++ CHANGES.rst | 3 ++- box/__init__.py | 2 +- setup.py | 1 - 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 1fb7e2d..20dc798 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -29,6 +29,8 @@ Code contributions: - Ivan Pepelnjak (ipspace) - Michał Górny (mgorny) - Serge Lu (Serge45) +- Eric Prestat (ericpre) + Suggestions and bug reporting: @@ -87,3 +89,5 @@ Suggestions and bug reporting: - Peter B (barmettl) - Ash A. (dragonpaw) - Коптев Роман Викторович (romikforest) +- lei wang (191801737) +- d00m514y3r \ No newline at end of file diff --git a/CHANGES.rst b/CHANGES.rst index 813368b..4712272 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,8 +4,9 @@ Changelog Version 7.1.0 ------------- +* Adding #255 defer ipython import for large import speed improvements (thanks to Eric Prestat) * Fixing #253 merge_update box list merge types not populated to sub dictionaries (thanks to lei wang) -* Removing #251 support for circular references in lists +* Removing #251 support for circular references in lists (thanks to d00m514y3r) Version 7.0.1 ------------- diff --git a/box/__init__.py b/box/__init__.py index b2225bb..64d0ca8 100644 --- a/box/__init__.py +++ b/box/__init__.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- __author__ = "Chris Griffith" -__version__ = "7.0.1" +__version__ = "7.1.0" from box.box import Box from box.box_list import BoxList diff --git a/setup.py b/setup.py index 0f0519e..148dac0 100644 --- a/setup.py +++ b/setup.py @@ -51,7 +51,6 @@ classifiers=[ "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", From 755ea4fcb3fa15dc7dd5839f686cbed91a7625b2 Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Thu, 10 Aug 2023 22:10:16 -0500 Subject: [PATCH 05/13] =?UTF-8?q?*=20Adding=20testing=20for=20Python=203.1?= =?UTF-8?q?2=20*=20Fixing=20#257=20Two=20test=20failures=20due=20to=20argu?= =?UTF-8?q?ments=20having=20incorrect=20types=20(thanks=20to=20Micha=C5=82?= =?UTF-8?q?=20G=C3=B3rny)=20*=20Fixing=20stub=20files=20to=20match=20lates?= =?UTF-8?q?t=20code=20signatures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pythonpublish.yml | 6 +- .github/workflows/tests.yml | 18 ++--- .pre-commit-config.yaml | 4 +- AUTHORS.rst | 2 +- CHANGES.rst | 3 + box/box.py | 10 +-- box/box.pyi | 104 +++++++++++++++------------- box/box_list.py | 8 +-- box/box_list.pyi | 38 +++++----- box/config_box.py | 3 +- box/config_box.pyi | 8 +-- box/converters.py | 14 +++- box/converters.pyi | 84 +++++++++++----------- box/from_file.pyi | 15 ++-- box/shorthand_box.py | 15 ++-- box/shorthand_box.pyi | 14 ++-- setup.py | 1 + test/test_box.py | 15 ++-- test/test_box_list.py | 2 +- 19 files changed, 192 insertions(+), 172 deletions(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index f7d8c27..4319897 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.11' - name: Install Dependencies run: | @@ -67,11 +67,11 @@ jobs: - name: Set up Python 3.10 uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: "3.11" - uses: RalfG/python-wheels-manylinux-build@v0.7.1-manylinux2010_x86_64 with: - python-versions: 'cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310' + python-versions: 'cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311' build-requirements: 'cython' - name: Install dependencies diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 35eff83..290647a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: package-checks: strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "pypy-3.8"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12-dev", "pypy-3.8"] os: [ubuntu-latest, macos-11, windows-latest] runs-on: ${{ matrix.os }} steps: @@ -49,14 +49,14 @@ jobs: run: | pip install dist/*.whl rm -rf box - python -m pytest + python -m pytest -vv - name: Test packaged wheel on Windows if: matrix.os == 'windows-latest' run: | $wheel = (Get-ChildItem dist\*.whl | Sort lastWriteTime | Select-Object -last 1).Name pip install dist\${wheel} Remove-item box -recurse -force - python -m pytest + python -m pytest -vv - name: Upload wheel artifact uses: actions/upload-artifact@v2 with: @@ -67,10 +67,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up Python 3.10 + - name: Set up Python 3.11 uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: "3.11" - uses: actions/cache@v3 with: @@ -86,14 +86,14 @@ jobs: - uses: RalfG/python-wheels-manylinux-build@v0.7.1-manylinux2010_x86_64 with: - python-versions: 'cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310' + python-versions: 'cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311' build-requirements: 'cython' - name: Test packaged wheel on linux run: | pip install dist/*cp310-manylinux*.whl rm -rf box - python -m pytest + python -m pytest -vv - name: Upload wheel artifact uses: actions/upload-artifact@v2 @@ -104,7 +104,7 @@ jobs: test: strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12-dev"] os: [ubuntu-latest, macos-11, windows-latest] runs-on: ${{ matrix.os }} steps: @@ -128,4 +128,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - pytest --cov=box test/ + pytest --cov=box -vv test/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2fc0ba4..eb3c1f0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: exclude: ^test/data/.+ - repo: https://github.com/ambv/black - rev: 23.1.0 + rev: 23.7.0 hooks: - id: black args: [--config=.black.toml] @@ -51,7 +51,7 @@ repos: always_run: true - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v1.0.1' + rev: 'v1.4.1' hooks: - id: mypy types: [python] diff --git a/AUTHORS.rst b/AUTHORS.rst index 20dc798..b67a735 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -90,4 +90,4 @@ Suggestions and bug reporting: - Ash A. (dragonpaw) - Коптев Роман Викторович (romikforest) - lei wang (191801737) -- d00m514y3r \ No newline at end of file +- d00m514y3r diff --git a/CHANGES.rst b/CHANGES.rst index 4712272..32ec496 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,10 @@ Version 7.1.0 ------------- * Adding #255 defer ipython import for large import speed improvements (thanks to Eric Prestat) +* Adding testing for Python 3.12 * Fixing #253 merge_update box list merge types not populated to sub dictionaries (thanks to lei wang) +* Fixing #257 Two test failures due to arguments having incorrect types (thanks to Michał Górny) +* Fixing stub files to match latest code signatures * Removing #251 support for circular references in lists (thanks to d00m514y3r) Version 7.0.1 diff --git a/box/box.py b/box/box.py index 354c7a7..391c50a 100644 --- a/box/box.py +++ b/box/box.py @@ -10,7 +10,7 @@ import warnings from keyword import iskeyword from os import PathLike -from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union +from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union, Literal from inspect import signature try: @@ -57,7 +57,7 @@ def _is_ipython(): ipython = False else: ipython = True if get_ipython() else False - + return ipython @@ -206,7 +206,7 @@ def __new__( box_recast: Optional[Dict] = None, box_dots: bool = False, box_class: Optional[Union[Dict, Type["Box"]]] = None, - box_namespace: Tuple[str, ...] = (), + box_namespace: Union[Tuple[str, ...], Literal[False]] = (), **kwargs: Any, ): """ @@ -253,7 +253,7 @@ def __init__( box_recast: Optional[Dict] = None, box_dots: bool = False, box_class: Optional[Union[Dict, Type["Box"]]] = None, - box_namespace: Tuple[str, ...] = (), + box_namespace: Union[Tuple[str, ...], Literal[False]] = (), **kwargs: Any, ): super().__init__() @@ -385,7 +385,7 @@ def __hash__(self): return hashing raise BoxTypeError('unhashable type: "Box"') - def __dir__(self): + def __dir__(self) -> List[str]: items = set(super().__dir__()) # Only show items accessible by dot notation for key in self.keys(): diff --git a/box/box.pyi b/box/box.pyi index 02ba3d4..9d56ab3 100644 --- a/box/box.pyi +++ b/box/box.pyi @@ -1,10 +1,11 @@ +from _typeshed import Incomplete from collections.abc import Mapping from os import PathLike -from typing import Any, Dict, Generator, List, Optional, Tuple, Union +from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union, Literal class Box(dict): def __new__( - cls: Any, + cls, *args: Any, default_box: bool = ..., default_box_attr: Any = ..., @@ -17,11 +18,12 @@ class Box(dict): box_safe_prefix: str = ..., box_duplicates: str = ..., box_intact_types: Union[Tuple, List] = ..., - box_recast: Dict = ..., + box_recast: Optional[Dict] = ..., box_dots: bool = ..., - box_class: Union[Dict, Box] = ..., + box_class: Optional[Union[Dict, Type["Box"]]] = ..., + box_namespace: Union[Tuple[str, ...], Literal[False]] = ..., **kwargs: Any, - ) -> Any: ... + ): ... def __init__( self, *args: Any, @@ -36,82 +38,84 @@ class Box(dict): box_safe_prefix: str = ..., box_duplicates: str = ..., box_intact_types: Union[Tuple, List] = ..., - box_recast: Dict = ..., + box_recast: Optional[Dict] = ..., box_dots: bool = ..., - box_class: Union[Dict, Box] = ..., + box_class: Optional[Union[Dict, Type["Box"]]] = ..., + box_namespace: Union[Tuple[str, ...], Literal[False]] = ..., **kwargs: Any, ) -> None: ... - def __add__(self, other: Mapping[Any, Any]) -> Any: ... - def __radd__(self, other: Mapping[Any, Any]) -> Any: ... - def __iadd__(self, other: Mapping[Any, Any]) -> Any: ... - def __or__(self, other: Mapping[Any, Any]) -> Any: ... - def __ror__(self, other: Mapping[Any, Any]) -> Any: ... - def __ior__(self, other: Mapping[Any, Any]) -> Any: ... # type: ignore[override] - def __sub__(self, other: Mapping[Any, Any]) -> Any: ... - def __hash__(self) -> Any: ... # type: ignore[override] - def __dir__(self): ... - def keys(self, dotted: Union[bool] = ...) -> Any: ... - def items(self, dotted: Union[bool] = ...) -> Any: ... - def get(self, key: Any, default: Any = ...): ... + def __add__(self, other: Mapping[Any, Any]): ... + def __radd__(self, other: Mapping[Any, Any]): ... + def __iadd__(self, other: Mapping[Any, Any]): ... + def __or__(self, other: Mapping[Any, Any]): ... + def __ror__(self, other: Mapping[Any, Any]): ... + def __ior__(self, other: Mapping[Any, Any]): ... # type: ignore[override] + def __sub__(self, other: Mapping[Any, Any]): ... + def __hash__(self): ... + def __dir__(self) -> List[str]: ... + def __contains__(self, item) -> bool: ... + def keys(self, dotted: Union[bool] = ...): ... + def items(self, dotted: Union[bool] = ...): ... + def get(self, key, default=...): ... def copy(self) -> Box: ... def __copy__(self) -> Box: ... - def __deepcopy__(self, memodict: Any = ...) -> Box: ... - def __getitem__(self, item: Any, _ignore_default: bool = ...) -> Any: ... - def __getattr__(self, item: Any) -> Any: ... - def __setitem__(self, key: Any, value: Any): ... - def __setattr__(self, key: Any, value: Any): ... - def __delitem__(self, key: Any): ... - def __delattr__(self, item: Any) -> None: ... - def pop(self, key: Any, *args: Any): ... + def __deepcopy__(self, memodict: Incomplete | None = ...) -> Box: ... + def __getitem__(self, item, _ignore_default: bool = ...): ... + def __getattr__(self, item): ... + def __setitem__(self, key, value): ... + def __setattr__(self, key, value): ... + def __delitem__(self, key): ... + def __delattr__(self, item) -> None: ... + def pop(self, key, *args): ... def clear(self) -> None: ... def popitem(self): ... def __iter__(self) -> Generator: ... def __reversed__(self) -> Generator: ... def to_dict(self) -> Dict: ... - def update(self, __m: Optional[Any] = ..., **kwargs: Any) -> None: ... - def merge_update(self, __m: Optional[Any] = ..., **kwargs: Any) -> None: ... - def setdefault(self, item: Any, default: Optional[Any] = ...): ... + def update(self, *args, **kwargs) -> None: ... + def merge_update(self, *args, **kwargs) -> None: ... + def setdefault(self, item, default: Incomplete | None = ...): ... def to_json( - self, filename: Union[str, PathLike] = ..., encoding: str = ..., errors: str = ..., **json_kwargs: Any - ) -> Any: ... + self, filename: Optional[Union[str, PathLike]] = ..., encoding: str = ..., errors: str = ..., **json_kwargs + ): ... @classmethod def from_json( - cls: Any, - json_string: str = ..., - filename: Union[str, PathLike] = ..., + cls, + json_string: Optional[str] = ..., + filename: Optional[Union[str, PathLike]] = ..., encoding: str = ..., errors: str = ..., - **kwargs: Any, + **kwargs, ) -> Box: ... def to_yaml( self, - filename: Union[str, PathLike] = ..., + filename: Optional[Union[str, PathLike]] = ..., default_flow_style: bool = ..., encoding: str = ..., errors: str = ..., - **yaml_kwargs: Any, - ) -> Any: ... + **yaml_kwargs, + ): ... @classmethod def from_yaml( - cls: Any, - yaml_string: str = ..., - filename: Union[str, PathLike] = ..., + cls, + yaml_string: Optional[str] = ..., + filename: Optional[Union[str, PathLike]] = ..., encoding: str = ..., errors: str = ..., - **kwargs: Any, + **kwargs, ) -> Box: ... - def to_toml(self, filename: Union[str, PathLike] = ..., encoding: str = ..., errors: str = ...) -> Any: ... + def to_toml(self, filename: Optional[Union[str, PathLike]] = ..., encoding: str = ..., errors: str = ...): ... @classmethod def from_toml( - cls: Any, - toml_string: str = ..., - filename: Union[str, PathLike] = ..., + cls, + toml_string: Optional[str] = ..., + filename: Optional[Union[str, PathLike]] = ..., encoding: str = ..., errors: str = ..., - **kwargs: Any, + **kwargs, ) -> Box: ... - def to_msgpack(self, filename: Union[str, PathLike] = ..., **kwargs: Any) -> Any: ... + def to_msgpack(self, filename: Optional[Union[str, PathLike]] = ..., **kwargs): ... @classmethod def from_msgpack( - cls: Any, msgpack_bytes: bytes = ..., filename: Union[str, PathLike] = ..., **kwargs: Any + cls, msgpack_bytes: Optional[bytes] = ..., filename: Optional[Union[str, PathLike]] = ..., **kwargs ) -> Box: ... diff --git a/box/box_list.py b/box/box_list.py index b51f2fc..beb3ef8 100644 --- a/box/box_list.py +++ b/box/box_list.py @@ -5,7 +5,7 @@ import copy import re from os import PathLike -from typing import Optional, Iterable, Type, Union +from typing import Optional, Iterable, Type, Union, List, Any import box from box.converters import ( @@ -149,15 +149,15 @@ def __deepcopy__(self, memo=None): out.append(copy.deepcopy(k, memo=memo)) return out - def __hash__(self): + def __hash__(self) -> int: # type: ignore[override] if self.box_options.get("frozen_box"): hashing = 98765 hashing ^= hash(tuple(self)) return hashing raise BoxTypeError("unhashable type: 'BoxList'") - def to_list(self): - new_list = [] + def to_list(self) -> List: + new_list: List[Any] = [] for x in self: if x is self: new_list.append(new_list) diff --git a/box/box_list.pyi b/box/box_list.pyi index dbd2007..95a792c 100644 --- a/box/box_list.pyi +++ b/box/box_list.pyi @@ -1,19 +1,18 @@ import box from box.converters import ( - BOX_PARAMETERS, - msgpack_available, - yaml_available, - toml_read_library, - toml_write_library, + BOX_PARAMETERS as BOX_PARAMETERS, + msgpack_available as msgpack_available, + toml_read_library as toml_read_library, + toml_write_library as toml_write_library, + yaml_available as yaml_available, ) -from box.exceptions import BoxError as BoxError, BoxTypeError as BoxTypeError from os import PathLike as PathLike -from typing import Any, Iterable, Optional, Type, Union +from typing import Any, Iterable, Optional, Type, Union, List class BoxList(list): def __new__(cls, *args: Any, **kwargs: Any): ... - box_options: Any = ... - box_org_ref: Any = ... + box_options: Any + box_org_ref: Any def __init__(self, iterable: Iterable = ..., box_class: Type[box.Box] = ..., **box_options: Any) -> None: ... def __getitem__(self, item: Any): ... def __delitem__(self, key: Any): ... @@ -21,10 +20,10 @@ class BoxList(list): def append(self, p_object: Any) -> None: ... def extend(self, iterable: Any) -> None: ... def insert(self, index: Any, p_object: Any) -> None: ... - def __copy__(self): ... - def __deepcopy__(self, memo: Optional[Any] = ...): ... - def __hash__(self) -> Any: ... # type: ignore[override] - def to_list(self): ... + def __copy__(self) -> "BoxList": ... + def __deepcopy__(self, memo: Optional[Any] = ...) -> "BoxList": ... + def __hash__(self) -> int: ... # type: ignore[override] + def to_list(self) -> List: ... def to_json( self, filename: Union[str, PathLike] = ..., @@ -35,7 +34,7 @@ class BoxList(list): ) -> Any: ... @classmethod def from_json( - cls: Any, + cls, json_string: str = ..., filename: Union[str, PathLike] = ..., encoding: str = ..., @@ -53,7 +52,7 @@ class BoxList(list): ) -> Any: ... @classmethod def from_yaml( - cls: Any, + cls, yaml_string: str = ..., filename: Union[str, PathLike] = ..., encoding: str = ..., @@ -65,7 +64,7 @@ class BoxList(list): ) -> Any: ... @classmethod def from_toml( - cls: Any, + cls, toml_string: str = ..., filename: Union[str, PathLike] = ..., key_name: str = ..., @@ -75,12 +74,9 @@ class BoxList(list): ) -> Any: ... def to_msgpack(self, filename: Union[str, PathLike] = ..., **kwargs: Any) -> Any: ... @classmethod - def from_msgpack( - cls: Any, msgpack_bytes: bytes = ..., filename: Union[str, PathLike] = ..., **kwargs: Any - ) -> Any: ... + def from_msgpack(cls, msgpack_bytes: bytes = ..., filename: Union[str, PathLike] = ..., **kwargs: Any) -> Any: ... def to_csv(self, filename: Union[str, PathLike] = ..., encoding: str = ..., errors: str = ...) -> Any: ... @classmethod def from_csv( - cls: Any, csv_string: str = ..., filename: Union[str, PathLike] = ..., encoding: str = ..., errors: str = ... + cls, csv_string: str = ..., filename: Union[str, PathLike] = ..., encoding: str = ..., errors: str = ... ) -> Any: ... - def _dotted_helper(self) -> Any: ... diff --git a/box/config_box.py b/box/config_box.py index 0d42b3b..0202ca3 100644 --- a/box/config_box.py +++ b/box/config_box.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +from typing import List from box.box import Box @@ -29,7 +30,7 @@ def __getattr__(self, item): except AttributeError: return super().__getattr__(item.lower()) - def __dir__(self): + def __dir__(self) -> List[str]: return super().__dir__() + ["bool", "int", "float", "list", "getboolean", "getfloat", "getint"] def bool(self, item, default=None): diff --git a/box/config_box.pyi b/box/config_box.pyi index 75afb44..1022f1b 100644 --- a/box/config_box.pyi +++ b/box/config_box.pyi @@ -1,9 +1,9 @@ from box.box import Box as Box -from typing import Any, Optional +from typing import Any, Optional, List class ConfigBox(Box): def __getattr__(self, item: Any): ... - def __dir__(self): ... + def __dir__(self) -> List[str]: ... def bool(self, item: Any, default: Optional[Any] = ...): ... def int(self, item: Any, default: Optional[Any] = ...): ... def float(self, item: Any, default: Optional[Any] = ...): ... @@ -11,5 +11,5 @@ class ConfigBox(Box): def getboolean(self, item: Any, default: Optional[Any] = ...): ... def getint(self, item: Any, default: Optional[Any] = ...): ... def getfloat(self, item: Any, default: Optional[Any] = ...): ... - def copy(self): ... - def __copy__(self): ... + def copy(self) -> "ConfigBox": ... + def __copy__(self) -> "ConfigBox": ... diff --git a/box/converters.py b/box/converters.py index 29da488..3bb003b 100644 --- a/box/converters.py +++ b/box/converters.py @@ -5,7 +5,6 @@ import csv import json -import sys from io import StringIO from os import PathLike from pathlib import Path @@ -34,6 +33,19 @@ toml_write_library: Optional[Any] = None toml_decode_error: Optional[Callable] = None +__all__ = [ + "_to_json", + "_to_yaml", + "_to_toml", + "_to_csv", + "_to_msgpack", + "_from_json", + "_from_yaml", + "_from_toml", + "_from_csv", + "_from_msgpack", +] + class BoxTomlDecodeError(BoxError): """Toml Decode Error""" diff --git a/box/converters.pyi b/box/converters.pyi index 5f94f33..43d2020 100644 --- a/box/converters.pyi +++ b/box/converters.pyi @@ -1,6 +1,5 @@ -from box.exceptions import BoxError as BoxError -from os import PathLike as PathLike -from typing import Any, Union, Optional, Dict, Callable +from typing import Any, Callable, Optional, Union, Dict +from os import PathLike yaml_available: bool toml_available: bool @@ -10,57 +9,52 @@ toml_read_library: Optional[Any] toml_write_library: Optional[Any] toml_decode_error: Optional[Callable] -def _exists(filename: Union[str, PathLike], create: bool = False) -> Any: ... def _to_json( - obj, filename: Optional[Union[str, PathLike]] = None, encoding: str = "utf-8", errors: str = "strict", **json_kwargs -) -> Any: ... + obj, filename: Optional[Union[str, PathLike]] = ..., encoding: str = ..., errors: str = ..., **json_kwargs +): ... def _from_json( - json_string: Optional[str] = None, - filename: Optional[Union[str, PathLike]] = None, - encoding: str = "utf-8", - errors: str = "strict", - multiline: bool = False, + json_string: Optional[str] = ..., + filename: Optional[Union[str, PathLike]] = ..., + encoding: str = ..., + errors: str = ..., + multiline: bool = ..., **kwargs, -) -> Any: ... +): ... def _to_yaml( obj, - filename: Optional[Union[str, PathLike]] = None, - default_flow_style: bool = False, - encoding: str = "utf-8", - errors: str = "strict", - ruamel_typ: str = "rt", - ruamel_attrs: Optional[Dict] = None, + filename: Optional[Union[str, PathLike]] = ..., + default_flow_style: bool = ..., + encoding: str = ..., + errors: str = ..., + ruamel_typ: str = ..., + ruamel_attrs: Optional[Dict] = ..., **yaml_kwargs, -) -> Any: ... +): ... def _from_yaml( - yaml_string: Optional[str] = None, - filename: Optional[Union[str, PathLike]] = None, - encoding: str = "utf-8", - errors: str = "strict", - ruamel_typ: str = "rt", - ruamel_attrs: Optional[Dict] = None, + yaml_string: Optional[str] = ..., + filename: Optional[Union[str, PathLike]] = ..., + encoding: str = ..., + errors: str = ..., + ruamel_typ: str = ..., + ruamel_attrs: Optional[Dict] = ..., **kwargs, -) -> Any: ... -def _to_toml( - obj, filename: Optional[Union[str, PathLike]] = None, encoding: str = "utf-8", errors: str = "strict" -) -> Any: ... +): ... +def _to_toml(obj, filename: Optional[Union[str, PathLike]] = ..., encoding: str = ..., errors: str = ...): ... def _from_toml( - toml_string: Optional[str] = None, - filename: Optional[Union[str, PathLike]] = None, - encoding: str = "utf-8", - errors: str = "strict", -) -> Any: ... -def _to_msgpack(obj, filename: Optional[Union[str, PathLike]] = None, **kwargs) -> Any: ... -def _from_msgpack( - msgpack_bytes: Optional[bytes] = None, filename: Optional[Union[str, PathLike]] = None, **kwargs -) -> Any: ... + toml_string: Optional[str] = ..., + filename: Optional[Union[str, PathLike]] = ..., + encoding: str = ..., + errors: str = ..., +): ... +def _to_msgpack(obj, filename: Optional[Union[str, PathLike]] = ..., **kwargs): ... +def _from_msgpack(msgpack_bytes: Optional[bytes] = ..., filename: Optional[Union[str, PathLike]] = ..., **kwargs): ... def _to_csv( - box_list, filename: Optional[Union[str, PathLike]] = None, encoding: str = "utf-8", errors: str = "strict", **kwargs -) -> Any: ... + box_list, filename: Optional[Union[str, PathLike]] = ..., encoding: str = ..., errors: str = ..., **kwargs +): ... def _from_csv( - csv_string: Optional[str] = None, - filename: Optional[Union[str, PathLike]] = None, - encoding: str = "utf-8", - errors: str = "strict", + csv_string: Optional[str] = ..., + filename: Optional[Union[str, PathLike]] = ..., + encoding: str = ..., + errors: str = ..., **kwargs, -) -> Any: ... +): ... diff --git a/box/from_file.pyi b/box/from_file.pyi index 00657eb..9e8be8a 100644 --- a/box/from_file.pyi +++ b/box/from_file.pyi @@ -1,9 +1,16 @@ -from box.box import Box -from box.box_list import BoxList +from box.box import Box as Box +from box.box_list import BoxList as BoxList from os import PathLike from typing import Any, Union def box_from_file( - file: Union[str, PathLike], file_type: str = ..., encoding: str = ..., errors: str = ..., **kwargs: Any + file: Union[str, PathLike], + file_type: str = ..., + encoding: str = ..., + errors: str = ..., + **kwargs: Any, +) -> Union[Box, BoxList]: ... +def box_from_string( + content: str, + string_type: str = ..., ) -> Union[Box, BoxList]: ... -def box_from_string(content: str, string_type: str = ...) -> Union[Box, BoxList]: ... diff --git a/box/shorthand_box.py b/box/shorthand_box.py index 5d24604..99dfc8d 100644 --- a/box/shorthand_box.py +++ b/box/shorthand_box.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +from typing import Dict from box.box import Box @@ -27,28 +28,28 @@ class SBox(Box): ] @property - def dict(self): + def dict(self) -> Dict: return self.to_dict() @property - def json(self): + def json(self) -> str: return self.to_json() @property - def yaml(self): + def yaml(self) -> str: return self.to_yaml() @property - def toml(self): + def toml(self) -> str: return self.to_toml() def __repr__(self): return f"SBox({self})" - def copy(self): + def copy(self) -> "SBox": return SBox(super(SBox, self).copy()) - def __copy__(self): + def __copy__(self) -> "SBox": return SBox(super(SBox, self).copy()) @@ -64,5 +65,5 @@ def __new__(cls, *args, **kwargs): obj._box_config["default_box"] = True return obj - def __repr__(self): + def __repr__(self) -> str: return f"DDBox({self})" diff --git a/box/shorthand_box.pyi b/box/shorthand_box.pyi index 4b86f73..deef693 100644 --- a/box/shorthand_box.pyi +++ b/box/shorthand_box.pyi @@ -1,15 +1,17 @@ +from typing import Dict + from box.box import Box as Box class SBox(Box): @property - def dict(self): ... + def dict(self) -> Dict: ... @property - def json(self): ... + def json(self) -> str: ... @property - def yaml(self): ... + def yaml(self) -> str: ... @property - def toml(self): ... - def copy(self): ... - def __copy__(self): ... + def toml(self) -> str: ... + def copy(self) -> "SBox": ... + def __copy__(self) -> "SBox": ... class DDBox(Box): ... diff --git a/setup.py b/setup.py index 148dac0..cb05f11 100644 --- a/setup.py +++ b/setup.py @@ -56,6 +56,7 @@ "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", "Development Status :: 5 - Production/Stable", "Natural Language :: English", diff --git a/test/test_box.py b/test/test_box.py index 3fd1ab0..d9012e9 100644 --- a/test/test_box.py +++ b/test/test_box.py @@ -1255,18 +1255,17 @@ def test_merge_list_options(self): a.merge_update({"key1": {"new": 5}, "Key 2": {"add_key": 6}, "lister": ["a"]}) assert a.lister == ["a"] - d1 = {'app': {'S3': {'S3Service': [{'bucket': 'bucket001'}]}}} + d1 = {"app": {"S3": {"S3Service": [{"bucket": "bucket001"}]}}} - d2 = {'app': {'S3': {'S3Service': [{'expirationDate': '2099-10-25'}]}}} + d2 = {"app": {"S3": {"S3Service": [{"expirationDate": "2099-10-25"}]}}} box1 = Box(d1) - box1.merge_update(d2, box_merge_lists='extend') - - assert box1 == Box({'app': {'S3': {'S3Service': [{'bucket': 'bucket001'}, {'expirationDate': '2099-10-25'}]}}}), box1 - - + box1.merge_update(d2, box_merge_lists="extend") + assert box1 == Box( + {"app": {"S3": {"S3Service": [{"bucket": "bucket001"}, {"expirationDate": "2099-10-25"}]}}} + ), box1 def test_box_from_empty_yaml(self): out = Box.from_yaml("---") @@ -1335,7 +1334,7 @@ def test_box_kwargs_should_not_be_included(self): "box_dots": True, "modify_tuples_box": True, "box_intact_types": (), - "box_recast": True, + "box_recast": {"id": int}, } bx = Box(**params) diff --git a/test/test_box_list.py b/test/test_box_list.py index 0fd05d3..73cd5d7 100644 --- a/test/test_box_list.py +++ b/test/test_box_list.py @@ -224,7 +224,7 @@ def test_box_config_propagate(self): def test_no_recursion_errors(self): a = Box({"list_of_dicts": [[{"example1": 1}]]}) a.list_of_dicts.append([{"example2": 2}]) - assert a['list_of_dicts'][1] == [{"example2": 2}] + assert a["list_of_dicts"][1] == [{"example2": 2}] def test_no_circular_references(self): circular_list = [] From 09dbb670aec732bd5eb0b15be61cd2d989739054 Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Thu, 10 Aug 2023 22:13:59 -0500 Subject: [PATCH 06/13] * Removing support for Python 3.7 as it is EOL --- .github/workflows/pythonpublish.yml | 2 +- .github/workflows/tests.yml | 4 ++-- CHANGES.rst | 1 + setup.py | 3 +-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 4319897..1bbf18e 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -39,7 +39,7 @@ jobs: strategy: matrix: os: [macos-11, windows-latest] - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11"] runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 290647a..f98ad17 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: package-checks: strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12-dev", "pypy-3.8"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12-dev", "pypy-3.8"] os: [ubuntu-latest, macos-11, windows-latest] runs-on: ${{ matrix.os }} steps: @@ -104,7 +104,7 @@ jobs: test: strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12-dev"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12-dev"] os: [ubuntu-latest, macos-11, windows-latest] runs-on: ${{ matrix.os }} steps: diff --git a/CHANGES.rst b/CHANGES.rst index 32ec496..20fc77f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,7 @@ Version 7.1.0 * Fixing #257 Two test failures due to arguments having incorrect types (thanks to Michał Górny) * Fixing stub files to match latest code signatures * Removing #251 support for circular references in lists (thanks to d00m514y3r) +* Removing support for Python 3.7 as it is EOL Version 7.0.1 ------------- diff --git a/setup.py b/setup.py index cb05f11..3781c42 100644 --- a/setup.py +++ b/setup.py @@ -45,13 +45,12 @@ py_modules=["box"], packages=["box"], ext_modules=extra, - python_requires=">=3.7", + python_requires=">=3.8", include_package_data=True, platforms="any", classifiers=[ "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", From fea74db7ab87a08aef303bc229f1e48e64314ce6 Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Thu, 10 Aug 2023 22:36:02 -0500 Subject: [PATCH 07/13] Adding slice fix for Python 3.12 --- .github/workflows/tests.yml | 2 +- box/box.py | 6 ++++++ box/box_list.py | 2 +- box/box_list.pyi | 1 + 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f98ad17..0341fd5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -104,7 +104,7 @@ jobs: test: strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12-dev"] + python-version: ["3.8", "3.9", "3.10", "3.11"] os: [ubuntu-latest, macos-11, windows-latest] runs-on: ${{ matrix.os }} steps: diff --git a/box/box.py b/box/box.py index 391c50a..158a55e 100644 --- a/box/box.py +++ b/box/box.py @@ -594,6 +594,12 @@ def __getitem__(self, item, _ignore_default=False): if item == "_box_config": cause = _exception_cause(err) raise BoxKeyError("_box_config should only exist as an attribute and is never defaulted") from cause + if isinstance(item, slice): + # In Python 3.12 this changes to a KeyError instead of TypeError + new_box = self._box_config["box_class"](**self.__box_config()) + for x in list(super().keys())[item.start : item.stop : item.step]: + new_box[x] = self[x] + return new_box if self._box_config["box_dots"] and isinstance(item, str) and ("." in item or "[" in item): try: first_item, children = _parse_box_dots(self, item) diff --git a/box/box_list.py b/box/box_list.py index beb3ef8..048b014 100644 --- a/box/box_list.py +++ b/box/box_list.py @@ -116,7 +116,7 @@ def extend(self, iterable): def insert(self, index, p_object): super().insert(index, self._convert(p_object)) - def _dotted_helper(self): + def _dotted_helper(self) -> List[str]: keys = [] for idx, item in enumerate(self): added = False diff --git a/box/box_list.pyi b/box/box_list.pyi index 95a792c..982093f 100644 --- a/box/box_list.pyi +++ b/box/box_list.pyi @@ -24,6 +24,7 @@ class BoxList(list): def __deepcopy__(self, memo: Optional[Any] = ...) -> "BoxList": ... def __hash__(self) -> int: ... # type: ignore[override] def to_list(self) -> List: ... + def _dotted_helper(self) -> List[str]: ... def to_json( self, filename: Union[str, PathLike] = ..., From 6d7db0f79d6e5fb2ae3221d179cea2bdacc15798 Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Thu, 10 Aug 2023 23:03:15 -0500 Subject: [PATCH 08/13] Removing packaging 3.7, lowering in workflow recursion limit --- .github/workflows/pythonpublish.yml | 2 +- .github/workflows/tests.yml | 2 +- test/test_box_list.py | 14 ++++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 1bbf18e..01e8b02 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -71,7 +71,7 @@ jobs: - uses: RalfG/python-wheels-manylinux-build@v0.7.1-manylinux2010_x86_64 with: - python-versions: 'cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311' + python-versions: 'cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311' build-requirements: 'cython' - name: Install dependencies diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0341fd5..a35035b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -86,7 +86,7 @@ jobs: - uses: RalfG/python-wheels-manylinux-build@v0.7.1-manylinux2010_x86_64 with: - python-versions: 'cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311' + python-versions: 'cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311' build-requirements: 'cython' - name: Test packaged wheel on linux diff --git a/test/test_box_list.py b/test/test_box_list.py index 73cd5d7..6ab22a1 100644 --- a/test/test_box_list.py +++ b/test/test_box_list.py @@ -5,6 +5,7 @@ import json import os import shutil +import sys from pathlib import Path from io import StringIO from test.common import test_root, tmp_dir @@ -227,7 +228,12 @@ def test_no_recursion_errors(self): assert a["list_of_dicts"][1] == [{"example2": 2}] def test_no_circular_references(self): - circular_list = [] - circular_list.append(circular_list) - with pytest.raises(RecursionError): - BoxList(circular_list) + og_limit = sys.getrecursionlimit() + sys.setrecursionlimit(100) + try: + circular_list = [] + circular_list.append(circular_list) + with pytest.raises(RecursionError): + BoxList(circular_list) + finally: + sys.setrecursionlimit(og_limit) From 2d6475a1b2548d278bbf53a6422f2a906bcef8dd Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Thu, 10 Aug 2023 23:22:56 -0500 Subject: [PATCH 09/13] Try changing to cibuildwheel --- .github/workflows/pythonpublish.yml | 13 ++++++------- .github/workflows/tests.yml | 18 +++++++++++------- test/test_box_list.py | 4 +++- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 01e8b02..3b8db76 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -69,15 +69,14 @@ jobs: with: python-version: "3.11" - - uses: RalfG/python-wheels-manylinux-build@v0.7.1-manylinux2010_x86_64 - with: - python-versions: 'cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311' - build-requirements: 'cython' - - - name: Install dependencies + - name: Build wheels run: | python -m pip install --upgrade pip - pip install twine --upgrade + pip install cibuildwheel twine --upgrade + python -m cibuildwheel --output-dir dist + env: + CIBW_BUILD: cp38-manylinux* cp39-manylinux* cp310-manylinux* cp311-manylinux* cp312-manylinux* + CIBW_BEFORE_BUILD: pip install Cython - name: Publish env: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a35035b..5bd3215 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -77,21 +77,25 @@ jobs: path: ~/.cache/pip key: package-manylinux-check-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements-test.txt') }} - - name: Install dependencies + - name: Build wheels run: | python -m pip install --upgrade pip + pip install cibuildwheel + python -m cibuildwheel --output-dir dist + env: + CIBW_BUILD: cp38-manylinux* cp39-manylinux* cp310-manylinux* cp311-manylinux* cp312-manylinux* + CIBW_BEFORE_BUILD: pip install Cython + + - name: Install test dependencies + run: | pip install -r requirements.txt pip install -r requirements-test.txt pip install coveralls flake8 flake8-print mypy setuptools wheel twine Cython - - uses: RalfG/python-wheels-manylinux-build@v0.7.1-manylinux2010_x86_64 - with: - python-versions: 'cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311' - build-requirements: 'cython' - - name: Test packaged wheel on linux run: | - pip install dist/*cp310-manylinux*.whl + ls dist/* + pip install dist/*311*.whl rm -rf box python -m pytest -vv diff --git a/test/test_box_list.py b/test/test_box_list.py index 6ab22a1..6d6cbcd 100644 --- a/test/test_box_list.py +++ b/test/test_box_list.py @@ -6,6 +6,7 @@ import os import shutil import sys +import platform from pathlib import Path from io import StringIO from test.common import test_root, tmp_dir @@ -229,7 +230,8 @@ def test_no_recursion_errors(self): def test_no_circular_references(self): og_limit = sys.getrecursionlimit() - sys.setrecursionlimit(100) + if platform.python_implementation() == "CPython": + sys.setrecursionlimit(200) try: circular_list = [] circular_list.append(circular_list) From f4c2a14da430bf95b89b4d481c880088bec8a4df Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Thu, 10 Aug 2023 23:47:02 -0500 Subject: [PATCH 10/13] Only build x86 Cython, add test commands for cibuildwheel --- .github/workflows/pythonpublish.yml | 9 ++++++--- .github/workflows/tests.yml | 17 +++-------------- test/test_box_list.py | 16 ++++++---------- 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 3b8db76..623e67c 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -64,7 +64,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up Python 3.10 + - name: Set up Python 3.11 uses: actions/setup-python@v4 with: python-version: "3.11" @@ -72,11 +72,13 @@ jobs: - name: Build wheels run: | python -m pip install --upgrade pip - pip install cibuildwheel twine --upgrade + pip install cibuildwheel python -m cibuildwheel --output-dir dist env: - CIBW_BUILD: cp38-manylinux* cp39-manylinux* cp310-manylinux* cp311-manylinux* cp312-manylinux* + CIBW_BUILD: cp38-manylinux_x86_64 cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 cp311-macosx_x86_64 cp312-macosx_x86_64 CIBW_BEFORE_BUILD: pip install Cython + CIBW_BEFORE_TEST: pip install -r requirements.txt -r requirements-test.txt setuptools wheel twine + CIBW_TEST_COMMAND: pytest {package}/test -vv - name: Publish env: @@ -84,3 +86,4 @@ jobs: TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | twine upload dist/*-manylinux*.whl + twine upload dist/*-macosx*.whl diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5bd3215..ccbd622 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -83,21 +83,10 @@ jobs: pip install cibuildwheel python -m cibuildwheel --output-dir dist env: - CIBW_BUILD: cp38-manylinux* cp39-manylinux* cp310-manylinux* cp311-manylinux* cp312-manylinux* + CIBW_BUILD: cp38-manylinux_x86_64 cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 cp311-macosx_x86_64 cp312-macosx_x86_64 CIBW_BEFORE_BUILD: pip install Cython - - - name: Install test dependencies - run: | - pip install -r requirements.txt - pip install -r requirements-test.txt - pip install coveralls flake8 flake8-print mypy setuptools wheel twine Cython - - - name: Test packaged wheel on linux - run: | - ls dist/* - pip install dist/*311*.whl - rm -rf box - python -m pytest -vv + CIBW_BEFORE_TEST: pip install -r requirements.txt -r requirements-test.txt setuptools wheel twine + CIBW_TEST_COMMAND: pytest {package}/test -vv - name: Upload wheel artifact uses: actions/upload-artifact@v2 diff --git a/test/test_box_list.py b/test/test_box_list.py index 6d6cbcd..536520f 100644 --- a/test/test_box_list.py +++ b/test/test_box_list.py @@ -229,13 +229,9 @@ def test_no_recursion_errors(self): assert a["list_of_dicts"][1] == [{"example2": 2}] def test_no_circular_references(self): - og_limit = sys.getrecursionlimit() - if platform.python_implementation() == "CPython": - sys.setrecursionlimit(200) - try: - circular_list = [] - circular_list.append(circular_list) - with pytest.raises(RecursionError): - BoxList(circular_list) - finally: - sys.setrecursionlimit(og_limit) + if sys.version_info >= (3, 12) and sys.platform == "win32": + pytest.skip("Windows fatal exception: stack overflow on github actions") + circular_list = [] + circular_list.append(circular_list) + with pytest.raises(RecursionError): + BoxList(circular_list) From d46efe15ca50b32835453801d04140e4b191e918 Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Fri, 11 Aug 2023 00:08:47 -0500 Subject: [PATCH 11/13] Removing python 3.12 cython builds --- .github/workflows/pythonpublish.yml | 2 +- .github/workflows/tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 623e67c..01ade03 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -75,7 +75,7 @@ jobs: pip install cibuildwheel python -m cibuildwheel --output-dir dist env: - CIBW_BUILD: cp38-manylinux_x86_64 cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 cp311-macosx_x86_64 cp312-macosx_x86_64 + CIBW_BUILD: cp38-manylinux_x86_64 cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp311-macosx_x86_64 CIBW_BEFORE_BUILD: pip install Cython CIBW_BEFORE_TEST: pip install -r requirements.txt -r requirements-test.txt setuptools wheel twine CIBW_TEST_COMMAND: pytest {package}/test -vv diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ccbd622..5c7d45f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -83,7 +83,7 @@ jobs: pip install cibuildwheel python -m cibuildwheel --output-dir dist env: - CIBW_BUILD: cp38-manylinux_x86_64 cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 cp311-macosx_x86_64 cp312-macosx_x86_64 + CIBW_BUILD: cp38-manylinux_x86_64 cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp311-macosx_x86_64 CIBW_BEFORE_BUILD: pip install Cython CIBW_BEFORE_TEST: pip install -r requirements.txt -r requirements-test.txt setuptools wheel twine CIBW_TEST_COMMAND: pytest {package}/test -vv From 171f3d3a8d485fcf997821d5dcfd727c21b7bd19 Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Sat, 26 Aug 2023 08:50:04 -0500 Subject: [PATCH 12/13] Use new upload action v3 --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5c7d45f..61d7f57 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -58,7 +58,7 @@ jobs: Remove-item box -recurse -force python -m pytest -vv - name: Upload wheel artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: python_box path: dist/*.whl @@ -89,7 +89,7 @@ jobs: CIBW_TEST_COMMAND: pytest {package}/test -vv - name: Upload wheel artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: python_box path: dist/*-manylinux*.whl From 94d129b9509283a52d42bb1dbd232d857455ef6e Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Sat, 26 Aug 2023 09:03:02 -0500 Subject: [PATCH 13/13] Fix Cython 3.0.1 crashes due to cython issue #5643 --- .github/workflows/pythonpublish.yml | 4 ++-- .github/workflows/tests.yml | 6 +++--- requirements-dev.txt | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml index 01ade03..a9d01fc 100644 --- a/.github/workflows/pythonpublish.yml +++ b/.github/workflows/pythonpublish.yml @@ -51,7 +51,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install setuptools wheel twine Cython --upgrade + pip install setuptools wheel twine Cython==3.0.0 --upgrade - name: Build and publish env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} @@ -76,7 +76,7 @@ jobs: python -m cibuildwheel --output-dir dist env: CIBW_BUILD: cp38-manylinux_x86_64 cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp311-macosx_x86_64 - CIBW_BEFORE_BUILD: pip install Cython + CIBW_BEFORE_BUILD: pip install Cython==3.0.0 CIBW_BEFORE_TEST: pip install -r requirements.txt -r requirements-test.txt setuptools wheel twine CIBW_TEST_COMMAND: pytest {package}/test -vv diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 61d7f57..db506f9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -31,7 +31,7 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt pip install -r requirements-test.txt - pip install coveralls flake8 flake8-print mypy setuptools wheel twine Cython + pip install coveralls flake8 flake8-print mypy setuptools wheel twine Cython==3.0.0 - name: Lint with flake8 run: | # stop the build if there are Python syntax errors, undefined names or print statements @@ -84,7 +84,7 @@ jobs: python -m cibuildwheel --output-dir dist env: CIBW_BUILD: cp38-manylinux_x86_64 cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp311-macosx_x86_64 - CIBW_BEFORE_BUILD: pip install Cython + CIBW_BEFORE_BUILD: pip install Cython==3.0.0 CIBW_BEFORE_TEST: pip install -r requirements.txt -r requirements-test.txt setuptools wheel twine CIBW_TEST_COMMAND: pytest {package}/test -vv @@ -115,7 +115,7 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt pip install -r requirements-test.txt - pip install setuptools wheel Cython + pip install setuptools wheel Cython==3.0.0 python setup.py build_ext --inplace - name: Test with pytest env: diff --git a/requirements-dev.txt b/requirements-dev.txt index cb6ad4c..e41d73f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ # Files needed for pre-commit hooks black>=23.1.0 -Cython>=0.29 +Cython==3.0.0 mypy>=1.0.1 pre-commit>=2.21.0