diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml index 763bf4d5..9cfd4839 100644 --- a/.github/workflows/codspeed.yml +++ b/.github/workflows/codspeed.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 with: python-version: "3.9" @@ -25,28 +25,21 @@ jobs: - name: Install poetry run: | - curl -fsS https://install.python-poetry.org | python - -y - - - name: Update PATH - if: ${{ matrix.os != 'Windows' }} - run: echo "$HOME/.local/bin" >> $GITHUB_PATH + pipx install poetry>=2 - name: Configure poetry - run: poetry config virtualenvs.create false + run: poetry config virtualenvs.in-project true - name: Install dependencies run: poetry install --only test --only benchmark --only build -vvv --no-root - - name: Install project - run: poetry install --only test --only benchmark --only build -vvv --no-root - - name: Install pendulum and check extensions run: | poetry run pip install -e . -vvv - python -c 'import pendulum._pendulum' + poetry run python -c 'import pendulum._pendulum' - name: Run benchmarks uses: CodSpeedHQ/action@v3 with: token: ${{ secrets.CODSPEED_TOKEN }} - run: pytest tests/ --codspeed + run: poetry run pytest tests/ --codspeed diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ba9ff30b..e0b22a59 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,6 +9,7 @@ on: jobs: build: name: Build on ${{ matrix.platform || matrix.os }} (${{ matrix.target }} - ${{ matrix.manylinux || 'auto' }}) + environment: release strategy: fail-fast: false matrix: @@ -31,8 +32,6 @@ jobs: - os: ubuntu platform: linux target: aarch64 - # mimalloc not supported on manylinux2014 cross-compile container - extra-build-args: --no-default-features # musllinux - os: ubuntu platform: linux @@ -46,14 +45,10 @@ jobs: platform: linux target: ppc64le interpreter: 3.9 3.10 3.11 3.12 3.13 - # mimalloc not supported on manylinux2014 cross-compile container - extra-build-args: --no-default-features - os: ubuntu platform: linux target: s390x interpreter: 3.9 3.10 3.11 3.12 3.13 - # mimalloc not supported on manylinux2014 cross-compile container - extra-build-args: --no-default-features runs-on: ${{ matrix.os }}-latest steps: @@ -77,13 +72,14 @@ jobs: - run: ${{ matrix.ls || 'ls -lh' }} dist/ - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: - name: dist + name: dist-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.manylinux }} path: dist build_sdist: runs-on: ubuntu-latest + environment: release steps: - uses: actions/checkout@v3 - name: Build sdist @@ -92,30 +88,49 @@ jobs: command: sdist args: --out dist - name: Upload sdist - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: dist + name: dist-sdist + path: dist + + build_none: + runs-on: ubuntu-latest + environment: release + steps: + - uses: actions/checkout@v3 + - name: Install and configure Poetry + uses: snok/install-poetry@v1 + with: + version: 1.8.5 + - name: Install dependencies + run: poetry install --only main --only test --only typing --only build + - name: Run poetry build + run: poetry build + - name: Upload sdist + uses: actions/upload-artifact@v4 + with: + name: dist-any path: dist Release: - needs: [ build, build_sdist ] - if: success() && startsWith(github.ref, 'refs/tags/') + needs: [ build, build_sdist, build_none ] + if: success() runs-on: ubuntu-latest - + environment: release + permissions: + id-token: write + contents: write steps: - name: Checkout code uses: actions/checkout@v2 - name: Download artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: dist + pattern: dist* path: dist - - - name: Install Poetry - run: | - curl -fsS https://install.python-poetry.org | python - -y + merge-multiple: true - name: Update PATH run: echo "$HOME/.local/bin" >> $GITHUB_PATH @@ -134,12 +149,8 @@ jobs: uses: ncipollo/release-action@v1 with: artifacts: "dist/*" - token: ${{ secrets.GITHUB_TOKEN }} draft: false prerelease: steps.check-version.outputs.prerelease == 'true' - - name: Publish to PyPI - env: - POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }} - run: | - poetry publish + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 014c5011..d605b723 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,7 +33,7 @@ jobs: strategy: matrix: os: [Ubuntu, MacOS, Windows] - python-version: [3.9, "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] defaults: run: shell: bash diff --git a/Makefile b/Makefile index d6626b5c..575ab920 100644 --- a/Makefile +++ b/Makefile @@ -16,3 +16,18 @@ lint-rust: format-rust: cd rust && cargo fmt --all cd rust && cargo clippy --tests --fix --allow-dirty -- -D warnings + +dev: + poetry install --only main --only test --only typing --only build --only lint + poetry run maturin develop + +lint: + poetry run mypy + poetry run pre-commit run --all-files + +test: + PENDULUM_EXTENSIONS=0 poetry run pytest -q tests + poetry run pytest -q tests + +clean: + rm src/pendulum/*.so diff --git a/clock b/clock index acd8c558..ae1d27ca 100755 --- a/clock +++ b/clock @@ -190,10 +190,11 @@ translations = {{}} def format_dict(self, d, tab=1): s = ["{\n"] for k, v in d.items(): - if isinstance(v, (dict, LocaleDataDict)): - v = self.format_dict(v, tab + 1) - else: - v = repr(v) + v = ( + self.format_dict(v, tab + 1) + if isinstance(v, (dict, LocaleDataDict)) + else repr(v) + ) s.append(f"{' ' * tab}{k!r}: {v},\n") s.append(f'{" " * (tab - 1)}}}') diff --git a/poetry.lock b/poetry.lock index 9cc51c4d..de2c6ede 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.0.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "babel" @@ -223,7 +223,7 @@ version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" -groups = ["benchmark", "dev", "lint"] +groups = ["dev", "lint"] files = [ {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, @@ -232,7 +232,7 @@ files = [ [package.extras] docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] -typing = ["typing-extensions (>=4.12.2)"] +typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] [[package]] name = "ghp-import" @@ -284,12 +284,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -539,7 +539,7 @@ watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] [[package]] name = "mkdocs-get-deps" @@ -837,26 +837,33 @@ histogram = ["pygal", "pygaljs"] [[package]] name = "pytest-codspeed" -version = "3.0.0" +version = "3.2.0" description = "Pytest plugin to create CodSpeed benchmarks" optional = false python-versions = ">=3.9" groups = ["benchmark"] files = [ - {file = "pytest_codspeed-3.0.0-py3-none-any.whl", hash = "sha256:ab1b8cb9da72e0d394718333d1abc7bea38524e09fd4854bc70a91abbcdcb20e"}, - {file = "pytest_codspeed-3.0.0.tar.gz", hash = "sha256:c5b80100ea32dd44079bb2db298288763eb8fe859eafa1650a8711bd2c32fd06"}, + {file = "pytest_codspeed-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5165774424c7ab8db7e7acdb539763a0e5657996effefdf0664d7fd95158d34"}, + {file = "pytest_codspeed-3.2.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bd55f92d772592c04a55209950c50880413ae46876e66bd349ef157075ca26c"}, + {file = "pytest_codspeed-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf6f56067538f4892baa8d7ab5ef4e45bb59033be1ef18759a2c7fc55b32035"}, + {file = "pytest_codspeed-3.2.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:39a687b05c3d145642061b45ea78e47e12f13ce510104d1a2cda00eee0e36f58"}, + {file = "pytest_codspeed-3.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46a1afaaa1ac4c2ca5b0700d31ac46d80a27612961d031067d73c6ccbd8d3c2b"}, + {file = "pytest_codspeed-3.2.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c48ce3af3dfa78413ed3d69d1924043aa1519048dbff46edccf8f35a25dab3c2"}, + {file = "pytest_codspeed-3.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66692506d33453df48b36a84703448cb8b22953eea51f03fbb2eb758dc2bdc4f"}, + {file = "pytest_codspeed-3.2.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:479774f80d0bdfafa16112700df4dbd31bf2a6757fac74795fd79c0a7b3c389b"}, + {file = "pytest_codspeed-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:109f9f4dd1088019c3b3f887d003b7d65f98a7736ca1d457884f5aa293e8e81c"}, + {file = "pytest_codspeed-3.2.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2f69a03b52c9bb041aec1b8ee54b7b6c37a6d0a948786effa4c71157765b6da"}, + {file = "pytest_codspeed-3.2.0-py3-none-any.whl", hash = "sha256:54b5c2e986d6a28e7b0af11d610ea57bd5531cec8326abe486f1b55b09d91c39"}, + {file = "pytest_codspeed-3.2.0.tar.gz", hash = "sha256:f9d1b1a3b2c69cdc0490a1e8b1ced44bffbd0e8e21d81a7160cfdd923f6e8155"}, ] [package.dependencies] cffi = ">=1.17.1" -filelock = ">=3.12.2" importlib-metadata = {version = ">=8.5.0", markers = "python_version < \"3.10\""} pytest = ">=3.8" rich = ">=13.8.1" -setuptools = {version = "*", markers = "python_full_version >= \"3.12.0\""} [package.extras] -build = ["semver (>=3.0.2)"] compat = ["pytest-benchmark (>=5.0.0,<5.1.0)", "pytest-xdist (>=3.6.1,<3.7.0)"] lint = ["mypy (>=1.11.2,<1.12.0)", "ruff (>=0.6.5,<0.7.0)"] test = ["pytest (>=7.0,<8.0)", "pytest-cov (>=4.0.0,<4.1.0)"] @@ -1088,28 +1095,6 @@ typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.1 [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] -[[package]] -name = "setuptools" -version = "75.6.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.9" -groups = ["benchmark"] -markers = "python_full_version >= \"3.12.0\"" -files = [ - {file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"}, - {file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"] -core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"] - [[package]] name = "six" version = "1.16.0" @@ -1330,7 +1315,7 @@ platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [[package]] name = "watchdog" @@ -1389,11 +1374,11 @@ files = [ ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [extras] diff --git a/pyproject.toml b/pyproject.toml index c60c4ff6..7184690b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] dependencies = [ @@ -65,6 +66,8 @@ maturin = ">=1.0,<2.0" [tool.maturin] module-name = "pendulum._pendulum" +features = ["pyo3/extension-module"] +python-packages = ["pendulum"] [tool.ruff] fix = true diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 1119bc7b..7c2b6587 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -6,7 +6,6 @@ version = 3 name = "_pendulum" version = "3.0.0" dependencies = [ - "mimalloc", "pyo3", ] @@ -46,16 +45,6 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" -[[package]] -name = "libmimalloc-sys" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3979b5c37ece694f1f5e51e7ecc871fdb0f517ed04ee45f88d15d6d553cb9664" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "memoffset" version = "0.9.0" @@ -65,15 +54,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "mimalloc" -version = "0.1.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa01922b5ea280a911e323e4d2fd24b7fe5cc4042e0d2cda3c40775cdc4bdc9c" -dependencies = [ - "libmimalloc-sys", -] - [[package]] name = "once_cell" version = "1.17.1" @@ -97,9 +77,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.22.6" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884" +checksum = "e484fd2c8b4cb67ab05a318f1fd6fa8f199fcc30819f08f07d200809dba26c15" dependencies = [ "cfg-if", "indoc", @@ -115,9 +95,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.22.6" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38" +checksum = "dc0e0469a84f208e20044b98965e1561028180219e35352a2afaf2b942beff3b" dependencies = [ "once_cell", "python3-dll-a", @@ -126,9 +106,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.22.6" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636" +checksum = "eb1547a7f9966f6f1a0f0227564a9945fe36b90da5a93b3933fc3dc03fae372d" dependencies = [ "libc", "pyo3-build-config", @@ -136,9 +116,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.22.6" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453" +checksum = "fdb6da8ec6fa5cedd1626c886fc8749bdcbb09424a86461eb8cdf096b7c33257" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -148,9 +128,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.22.6" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe" +checksum = "38a385202ff5a92791168b1136afae5059d3ac118457bb7bc304c197c2d33e7d" dependencies = [ "heck", "proc-macro2", @@ -161,9 +141,9 @@ dependencies = [ [[package]] name = "python3-dll-a" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f07cd4412be8fa09a721d40007c483981bbe072cd6a21f2e83e04ec8f8343f" +checksum = "9b66f9171950e674e64bad3456e11bb3cca108e5c34844383cfe277f45c8a7a8" dependencies = [ "cc", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 31f55dc1..ed902e8c 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [lib] name = "_pendulum" -crate-type = ["cdylib", "rlib"] +crate-type = ["cdylib"] [profile.release] lto = "fat" @@ -14,9 +14,7 @@ strip = true overflow-checks = false [dependencies] -pyo3 = { version = "0.22", features = ["extension-module", "generate-import-lib"] } -mimalloc = { version = "0.1.39", optional = true, default-features = false } +pyo3 = { version = "0.23", features = ["extension-module", "generate-import-lib"] } [features] extension-module = ["pyo3/extension-module"] -default = ["mimalloc"] diff --git a/rust/src/lib.rs b/rust/src/lib.rs index bd0f1f6f..aac55da7 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,9 +1,5 @@ extern crate core; -#[cfg(feature = "mimalloc")] -#[global_allocator] -static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; - mod constants; mod helpers; mod parsing; diff --git a/rust/src/python/helpers.rs b/rust/src/python/helpers.rs index f4523426..958d5506 100644 --- a/rust/src/python/helpers.rs +++ b/rust/src/python/helpers.rs @@ -76,7 +76,7 @@ impl PartialOrd for DateTimeInfo<'_> { pub fn get_tz_name<'py>(dt: &Bound<'py, PyAny>) -> PyResult { // let tz: &str = ""; - if !PyDateTime::is_type_of_bound(dt) { + if !PyDateTime::is_type_of(dt) { return Ok(String::new()); } @@ -107,7 +107,7 @@ pub fn get_tz_name<'py>(dt: &Bound<'py, PyAny>) -> PyResult { } pub fn get_offset(dt: &Bound) -> PyResult { - if !PyDateTime::is_type_of_bound(dt) { + if !PyDateTime::is_type_of(dt) { return Ok(0); } @@ -170,7 +170,7 @@ pub fn precise_diff<'py>( total_seconds: 0, tz: dt1_tz.as_str(), offset: get_offset(dt1)?, - is_datetime: PyDateTime::is_type_of_bound(dt1), + is_datetime: PyDateTime::is_type_of(dt1), }; let mut dtinfo2 = DateTimeInfo { year: dt2.downcast::()?.get_year(), @@ -183,7 +183,7 @@ pub fn precise_diff<'py>( total_seconds: 0, tz: dt2_tz.as_str(), offset: get_offset(dt2)?, - is_datetime: PyDateTime::is_exact_type_of_bound(dt2), + is_datetime: PyDateTime::is_exact_type_of(dt2), }; let in_same_tz: bool = dtinfo1.tz == dtinfo2.tz && !dtinfo1.tz.is_empty(); let mut total_days = helpers::day_number(dtinfo2.year, dtinfo2.month as u8, dtinfo2.day as u8) diff --git a/rust/src/python/mod.rs b/rust/src/python/mod.rs index d62e45dc..6adb0277 100644 --- a/rust/src/python/mod.rs +++ b/rust/src/python/mod.rs @@ -20,8 +20,5 @@ pub fn _pendulum(_py: Python<'_>, m: &Bound) -> PyResult<()> { m.add_class::()?; m.add_class::()?; - #[cfg(not(feature = "mimalloc"))] - m.setattr("__pendulum_default_allocator__", true)?; // uses setattr so this is not in __all__ - Ok(()) } diff --git a/rust/src/python/parsing.rs b/rust/src/python/parsing.rs index cff608e5..993f7481 100644 --- a/rust/src/python/parsing.rs +++ b/rust/src/python/parsing.rs @@ -7,6 +7,9 @@ use pyo3::types::PyTime; use crate::parsing::Parser; use crate::python::types::{Duration, FixedTimezone}; +// TODO: pyO3 v0.23 deprecates `ToPyObject`, function below must be migrated as per +// https://pyo3.rs/v0.23.0/migration +#[allow(deprecated)] #[pyfunction] pub fn parse_iso8601(py: Python, input: &str) -> PyResult { let parsed = Parser::new(input).parse(); @@ -16,7 +19,7 @@ pub fn parse_iso8601(py: Python, input: &str) -> PyResult { (Some(datetime), None, None) => match (datetime.has_date, datetime.has_time) { (true, true) => match datetime.offset { Some(offset) => { - let dt = PyDateTime::new_bound( + let dt = PyDateTime::new( py, datetime.year as i32, datetime.month as u8, @@ -35,7 +38,7 @@ pub fn parse_iso8601(py: Python, input: &str) -> PyResult { Ok(dt.to_object(py)) } None => { - let dt = PyDateTime::new_bound( + let dt = PyDateTime::new( py, datetime.year as i32, datetime.month as u8, @@ -51,7 +54,7 @@ pub fn parse_iso8601(py: Python, input: &str) -> PyResult { } }, (true, false) => { - let dt = PyDate::new_bound( + let dt = PyDate::new( py, datetime.year as i32, datetime.month as u8, @@ -62,7 +65,7 @@ pub fn parse_iso8601(py: Python, input: &str) -> PyResult { } (false, true) => match datetime.offset { Some(offset) => { - let dt = PyTime::new_bound( + let dt = PyTime::new( py, datetime.hour as u8, datetime.minute as u8, @@ -78,7 +81,7 @@ pub fn parse_iso8601(py: Python, input: &str) -> PyResult { Ok(dt.to_object(py)) } None => { - let dt = PyTime::new_bound( + let dt = PyTime::new( py, datetime.hour as u8, datetime.minute as u8, diff --git a/rust/src/python/types/timezone.rs b/rust/src/python/types/timezone.rs index 58012710..5109f31e 100644 --- a/rust/src/python/types/timezone.rs +++ b/rust/src/python/types/timezone.rs @@ -21,7 +21,7 @@ impl FixedTimezone { py: Python<'p>, _dt: &Bound<'p, PyAny>, ) -> Result, PyErr> { - PyDelta::new_bound(py, 0, self.offset, 0, true) + PyDelta::new(py, 0, self.offset, 0, true) } fn tzname(&self, _dt: &Bound) -> String { @@ -33,7 +33,7 @@ impl FixedTimezone { py: Python<'p>, _dt: &Bound<'p, PyAny>, ) -> Result, PyErr> { - PyDelta::new_bound(py, 0, 0, 0, true) + PyDelta::new(py, 0, 0, 0, true) } fn __repr__(&self) -> String { diff --git a/src/pendulum/duration.py b/src/pendulum/duration.py index a4875fca..bd36539d 100644 --- a/src/pendulum/duration.py +++ b/src/pendulum/duration.py @@ -389,7 +389,8 @@ def __floordiv__(self, other: int | timedelta) -> int | Duration: usec = self._to_microseconds() if isinstance(other, timedelta): return cast( - int, usec // other._to_microseconds() # type: ignore[attr-defined] + int, + usec // other._to_microseconds(), # type: ignore[attr-defined] ) if isinstance(other, int): @@ -416,7 +417,8 @@ def __truediv__(self, other: int | float | timedelta) -> Self | float: usec = self._to_microseconds() if isinstance(other, timedelta): return cast( - float, usec / other._to_microseconds() # type: ignore[attr-defined] + float, + usec / other._to_microseconds(), # type: ignore[attr-defined] ) if isinstance(other, int): @@ -443,7 +445,7 @@ def __truediv__(self, other: int | float | timedelta) -> Self | float: def __mod__(self, other: timedelta) -> Self: if isinstance(other, timedelta): - r = self._to_microseconds() % other._to_microseconds() # type: ignore[attr-defined] # noqa: E501 + r = self._to_microseconds() % other._to_microseconds() # type: ignore[attr-defined] # noqa: E501 return self.__class__(0, 0, r) diff --git a/src/pendulum/formatting/formatter.py b/src/pendulum/formatting/formatter.py index 8735ecc4..ee098243 100644 --- a/src/pendulum/formatting/formatter.py +++ b/src/pendulum/formatting/formatter.py @@ -38,7 +38,7 @@ _MATCH_TIMESTAMP = r"[+-]?\d+(\.\d{1,6})?" _MATCH_WORD = ( "(?i)[0-9]*" - "['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+" + "['a-z\u00a0-\u05ff\u0700-\ud7ff\uf900-\ufdcf\ufdf0-\uffef]+" r"|[\u0600-\u06FF/]+(\s*?[\u0600-\u06FF]+){1,2}" ) _MATCH_TIMEZONE = "[A-Za-z0-9-+]+(/[A-Za-z0-9-+_]+)?" @@ -440,10 +440,9 @@ def _check_parsed( # we use it and don't go any further if parsed["timestamp"] is not None: str_us = str(parsed["timestamp"]) - if "." in str_us: - microseconds = int(f'{str_us.split(".")[1].ljust(6, "0")}') - else: - microseconds = 0 + microseconds = ( + int(f"{str_us.split('.')[1].ljust(6, '0')}") if "." in str_us else 0 + ) from pendulum.helpers import local_time diff --git a/src/pendulum/parser.py b/src/pendulum/parser.py index a329a321..82e1817b 100644 --- a/src/pendulum/parser.py +++ b/src/pendulum/parser.py @@ -1,6 +1,7 @@ from __future__ import annotations import datetime +import os import typing as t import pendulum @@ -17,7 +18,12 @@ from pendulum.interval import Interval from pendulum.time import Time +with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1" + try: + if not with_extensions: + raise ImportError() + from pendulum._pendulum import Duration as RustDuration except ImportError: RustDuration = None # type: ignore[assignment,misc] diff --git a/src/pendulum/parsing/__init__.py b/src/pendulum/parsing/__init__.py index 60b232bf..e8c6dd49 100644 --- a/src/pendulum/parsing/__init__.py +++ b/src/pendulum/parsing/__init__.py @@ -4,7 +4,6 @@ import copy import os import re -import struct from datetime import date from datetime import datetime @@ -21,7 +20,7 @@ with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1" try: - if not with_extensions or struct.calcsize("P") == 4: + if not with_extensions: raise ImportError() from pendulum._pendulum import Duration diff --git a/src/pendulum/tz/local_timezone.py b/src/pendulum/tz/local_timezone.py index e32f1fdf..12e85b94 100644 --- a/src/pendulum/tz/local_timezone.py +++ b/src/pendulum/tz/local_timezone.py @@ -210,7 +210,8 @@ def _get_unix_timezone(_root: str = "/") -> Timezone: line = line[match.end() :] etctz = line[ : cast( - re.Match, end_re.search(line) # type: ignore[type-arg] + re.Match, # type: ignore[type-arg] + end_re.search(line), ).start() ]