diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 4115cba..5326b84 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,4 +1,4 @@ -name: CI Tests +name: Tests # Controls when the workflow will run on: @@ -21,6 +21,7 @@ jobs: - "3.11" - "3.12" - "3.13" + - "3.14" steps: - uses: actions/checkout@v5 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 6b9d1ac..b4f678d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -13,12 +13,5 @@ jobs: - uses: actions/setup-python@v6 with: python-version: 3.x - - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV - - uses: actions/cache@v3 - with: - key: mkdocs-material-${{ env.cache_id }} - path: .cache - restore-keys: | - mkdocs-material- - - run: pip install mkdocs-material + - run: pip install -r ./docs/requirements.txt - run: mkdocs gh-deploy --force diff --git a/README.md b/README.md index cb37047..de4de57 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@  [](https://pypi.org/project/PyBackport/) -[](https://www.python.org/downloads/) +[](https://www.python.org/downloads/) [](https://github.com/Jtachan/PyBackport/blob/master/LICENSE) [](https://pypi.org/project/PyBackport/) [](https://Jtachan.github.io/PyBackport/) diff --git a/docs/builtins.md b/docs/builtins.md index 0804224..c4d5464 100644 --- a/docs/builtins.md +++ b/docs/builtins.md @@ -1,59 +1,3 @@ # PyBackport: Builtins -`py_back` allows using the `builtins` module just as the original. -However, they must be imported and initialized by converting the instance: - -```pycon -# Python version lower than 3.9 ->>> from py_back.builtins import str - ->>> my_string = str("Hello world!") ->>> print(my_string.removesuffix("!")) -Hello world -``` - -## str - -### [_str_.**removeprefix**(prefix, /)](https://docs.python.org/3/library/stdtypes.html#str.removeprefix) - -If the string starts with the prefix string, return string[len(prefix):]. Otherwise, return a copy of the original string: - -```pycon -from py_back.builtins import str - ->>> str('TestHook').removeprefix('Test') -'Hook' - ->>> str('BaseTestCase').removeprefix('Test') -'BaseTestCase' -``` - -> Backported from python 3.9. - -### [_str_.**removesuffix**(suffix, /)](https://docs.python.org/3/library/stdtypes.html#str.removesuffix) - -If the string ends with the suffix string and that suffix is not empty, return string[:-len(suffix)]. Otherwise, return a copy of the original string: - -```pycon -from py_back.builtins import str - ->>> str('MiscTests').removesuffix('Tests') -'Misc' - ->>> str('TmpDirMixin').removesuffix('Tests') -'TmpDirMixin' -``` - -## dict - -### [d | other](https://docs.python.org/3/library/stdtypes.html#typesmapping:~:text=values()%0AFalse-,d%20%7C%20other,-Create%20a%20new) - -Create a new dictionary with the merged keys and values of d and other, which must both be dictionaries. The values of other take priority when d and other share keys. - -> Backported from python 3.9 - -### [d |= other](https://docs.python.org/3/library/stdtypes.html#typesmapping:~:text=in%20version%203.9.-,d%20%7C%3D%20other,-Update%20the%20dictionary) - -Update the dictionary d with keys and values from other, which may be either a [mapping](https://docs.python.org/3/glossary.html#term-mapping) or an [iterable](https://docs.python.org/3/glossary.html#term-iterable) of key/value pairs. The values of other take priority when d and other share keys. - -> Backported from python 3.9 +::: py_back.builtins diff --git a/docs/enum.md b/docs/enum.md index 13dab06..08281b4 100644 --- a/docs/enum.md +++ b/docs/enum.md @@ -1,72 +1,3 @@ # PyBackport: Enumerations -`py_back` allows using the `enum` module just as the original, with the only difference at the import. - -```python -from py_back import enum - - -class Number(enum.IntEnum): - """Enumeration using the original 'IntEnum' call""" - ONE = enum.auto() - TWO = 2 - - -class Animal(enum.StrEnum): - """Supported original 'StrEnum' for python versions < 3.11""" - CAT = enum.auto() - DOG = "dog" -``` - ---- - -## [_enum._**IntEnum**](https://docs.python.org/3/library/enum.html#enum.IntEnum) - -> Backported from version 3.11: `str()` and `format()` - -_IntEnum_ is the same as _Enum_, but its members are also integers and can be used anywhere that an integer can be used. - -> **Note:** [`__str__()`](https://docs.python.org/3/reference/datamodel.html#object.__str__) is now `int.__str__()` to better support the replacement of existing constants use-case. -[`__format__()`](https://docs.python.org/3/reference/datamodel.html#object.__format__) was already `int.__format__()` for that same reason. - -## [_enum._**StrEnum**](https://docs.python.org/3.11/library/enum.html#enum.StrEnum) - -> Backported from version 3.11 - -_StrEnum_ is the same as [_Enum_](https://docs.python.org/3.12/library/enum.html#enum.Enum), but its members are also strings and can be used in most of the same places that a string can be used. - -```pycon ->>> from py_back import enum ->>> class Animal(enum.StrEnum): -... CAT = enum.auto() -... DOG = "dog" -... ->>> Animal.CAT -cat ->>> Animal.DOG.title() -'Dog' ->>> Animal.CAT == "cat" -True ->>> Animal.CAT + Animal.DOG -'catdog' ->>> " and ".join(list(Animals)) -'cat and dog' -``` - -**Note**: Using [`auto`](https://docs.python.org/3.12/library/enum.html#enum.auto) results in the lower-cased member name as the value. - -## [_enum._**IntFlag**](https://docs.python.org/3/library/enum.html#enum.IntFlag) - -> Backported from version 3.11: `str()` and `format()` - -IntFlag is the same as Flag, but its members are also integers and can be used anywhere that an integer can be used. - -> **None:** [`__str__()`](https://docs.python.org/3/reference/datamodel.html#object.__str__) is now `int.__str__()` to better support the replacement of existing constants use-case. -[`__format__()`](https://docs.python.org/3/reference/datamodel.html#object.__format__) was already `int.__format__()` for that same reason. - -## [_enum._**ReprEnum**](https://docs.python.org/3/library/enum.html#enum.ReprEnum) - -> Backported from version 3.11 - -_ReprEnum_ uses the repr() of Enum, but the str() of the mixed-in data type. -The class is used for any builtin type enum. +::: py_back.enum diff --git a/docs/index.md b/docs/index.md index dd9e940..9fc82f2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -5,7 +5,7 @@ _Python Backport_ main goal is to backport functionalities from newer python releases. It allows using its modules just as the original ones, with the only difference at the import. -Any backported (or experimental) functionality can be imported with the module `py_back`. +Any backported functionality can be imported with the module `py_back`. ```python from py_back import enum @@ -46,7 +46,3 @@ Alternatively, any pip-install-git command can be called over the repository. ```commandline pip install git+https://github.com/Jtachan/PyBackport.git ``` - ---- -## Modules index - - [enum](enum.md) diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..1f1a032 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +mkdocs-material +mkdocstrings-python +ruff \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 5679069..57c0492 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,7 +8,7 @@ nav: - Enum: "enum.md" theme: - name: material + name: readthedocs palette: - media: "(prefers-color-scheme: light)" scheme: default @@ -45,7 +45,38 @@ markdown_extensions: permalink: true plugins: - search - -extra: - version: - provider: mike + - autorefs + - mkdocstrings: + handlers: + python: # https://mkdocstrings.github.io/python/usage/ + options: + # General options + show_bases: true + show_source: false + # Headings + heading_level: 2 + parameter_headings: true + show_root_heading: true + show_root_full_path: true + show_root_members_full_path: false + show_category_heading: false + # Members + inherited_members: false + members_order: source +# filters: +# - "!^_.*$" + group_by_category: true + show_submodules: true + # Docstrings + docstring_style: numpy + docstring_section_style: table + merge_init_into_class: true + # Signatures + annotations_path: source + line_length: 88 + modernize_annotations: true + overloads_only: true + show_signature: true + show_signature_annotations: true + separate_signature: true + show_overloads: true diff --git a/ruff.toml b/ruff.toml index 0d05419..bd8e751 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,5 +1,5 @@ output-format = "concise" -line-length = 120 +line-length = 88 [format] docstring-code-format = true @@ -49,11 +49,22 @@ ignore = [ "PLR0917", # too-many-positional-arguments "PLC1901", # compare-to-empty-string "PLR2004", # magic-value-comparison + "RUF067", # non empty init module ] [lint.per-file-ignores] "tests/*" = [ - "A004","PLC0415", + "A004", # Shadowing builtins name +] +"src/**/builtins/*" = [ + "A001", # Builtin variable shadowing + "A004", # Shadowing a Python builtin through import + "N801", # Invalid class name (not CamelCase) +] +"src/**/__init__.py" = [ + "F401", # Imported but unused (false positive) + "F403", # Unable to detect undefined names wiht `from ... import *` + "F405", # Unable to detect (un)defined names due to `from ... import *` ] [lint.flake8-annotations] diff --git a/src/py_back/__init__.py b/src/py_back/__init__.py index 3d36aad..d52dc33 100644 --- a/src/py_back/__init__.py +++ b/src/py_back/__init__.py @@ -1,3 +1,14 @@ -"""Python package to port functionalities from newer python versions back to older ones.""" +"""Backporting functionalities from newer python versions back to older ones. -__version__ = "0.3.0-1" +The software was created by Jtachan and with MIT license. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +__version__ = "0.3.1" diff --git a/src/py_back/builtins/__init__.py b/src/py_back/builtins/__init__.py index 88817ee..acd7eea 100644 --- a/src/py_back/builtins/__init__.py +++ b/src/py_back/builtins/__init__.py @@ -1,17 +1,27 @@ -"""Module to backport 'builtins' classes depending on the system python.""" +"""Module to backport 'builtins' classes depending on the system python. + +`py_back` allows using the `builtins` module just as the original. + +```pycon +# Python version lower than 3.9 +>>> from py_back.builtins import str +>>> my_string = str("Hello world!") +>>> print(my_string.removesuffix("!")) +Hello world +``` + +!!! Note + Python builtins don't require to be imported, but the backported builtins do not + follow this rule. This results in the inconvenience that backported instances must + be defined with the constructor provided by `py_back`, even if they interact with + other not backported builtins. +""" import sys -import warnings -from builtins import * # noqa: F403 -__all__ = [name for name in dir() if not name.startswith("_")] +__all__ = ["dict", "str"] + +from builtins import * -if sys.version_info >= (3, 9): - warnings.warn( - "Importing from the standard builtins library: dict, str\n" - "Consider 'from builtins import ...' instead of 'from py_back.builtins " - "import ...'", - stacklevel=2, - ) -else: - from ._back_builtins import dict, str # noqa: F401, A004 +if sys.version_info < (3, 9): + from ._back_builtins import dict, str diff --git a/src/py_back/builtins/_back_builtins.py b/src/py_back/builtins/_back_builtins.py index 603813d..d3c4101 100644 --- a/src/py_back/builtins/_back_builtins.py +++ b/src/py_back/builtins/_back_builtins.py @@ -7,51 +7,85 @@ __all__ = ["dict", "str"] -class dict(builtins.dict): # noqa: A001, N801 +class dict(builtins.dict): """Backport for 'dict' class. - dict() -> new empty dictionary - dict(mapping) -> new dictionary initialized from a mapping object's - (key, value) pairs - dict(iterable) -> new dictionary initialized as if via: - d = {} - for k, v in iterable: - d[k] = v - dict(**kwargs) -> new dictionary initialized with the name=value pairs - in the keyword argument list. For example: dict(one=1, two=2) +
dict() -> new empty dictionary.dict(mapping) -> new dictionary initialized from a mapping
+ object's (key, value) pairs.dict(iterable) -> new dictionary initialized as if.dict(**kwargs) -> new dictionary initialized with the
+ name=value pairs in the keyword argument list. E.G.:
+ dict(one=1, two=2)str(object='') -> strstr(bytes_or_buffer[, encoding[, errors]]) ->
+ str