Skip to content

Tracking: repository modernization #277

Description

@jaysonsantos

This issue tracks the modernization of this repository. It links twelve work issues in four streams.

Four audits produced the work items. Each linked issue holds the evidence, the exact file and line references, and the acceptance criteria. Another contributor can pick up any issue without more context.

Decisions made up front

Two audits disagreed. These are the resolutions.

Minimum Python version: 3.10. Python 3.8 ended support in October 2024. Python 3.9 ended support in October 2025. uhashring 2.5, released 2026-08-03, declares Requires-Python: >=3.10. A lower floor forces a permanent uhashring<2.5 pin.

Use Renovate, not Dependabot. Renovate groups patch and minor updates into one pull request. Dependabot cannot group by update type. See #271.

Keep README.rst. Do not convert it to Markdown. README.rst is a symlink to docs/intro.rst. That file is a Sphinx source in reStructuredText. A conversion breaks the documentation build. The pandoc step in .github/workflows/publish.yml stays.

Streams

Stream 1: Python version and legacy code

Stream 2: Packaging and tooling

Stream 3: CI and release

Stream 4: Code quality

Order

#265 ──┬─> #266 ──> #267
       │      │
       │      ├─> #276
       │      └─────────────┐
       │                    │
       └─> #268 ──┬─> #269  │
                  ├─> #270  │
                  └─> #275 <┘
                        ^
#273 ───────────────────┘

#271 (independent)
#272 ──> (no dependents)
#274 (independent)

Read this as follows.

  1. Start with Set the minimum supported Python version to 3.10 #265. It sets the version floor that the other issues assume.
  2. Remove the six dependency and delete bmemcached/compat.py #266 depends on Set the minimum supported Python version to 3.10 #265. Remove the remaining Python 2 idioms #267 depends on Remove the six dependency and delete bmemcached/compat.py #266. Both touch the same lines of bmemcached/protocol.py.
  3. Migrate setup.py to a PEP 621 pyproject.toml #268 depends on Set the minimum supported Python version to 3.10 #265. Do Migrate setup.py to a PEP 621 pyproject.toml #268 before Replace flake8 with ruff and modernize pre-commit #269 and Replace requirements_test.txt with a PEP 735 dependency group #270. All three change pyproject.toml.
  4. Replace flake8 with ruff and modernize pre-commit #269 and Replace requirements_test.txt with a PEP 735 dependency group #270 both change tox.ini. Merge them in sequence.
  5. Add py.typed and annotate the leaf modules #275 depends on Remove the six dependency and delete bmemcached/compat.py #266, Migrate setup.py to a PEP 621 pyproject.toml #268, and Fix five defects: assert guards, exception message, password leak, circular import #273.
  6. Remove per-call struct format strings from the response parse path #276 depends on Remove the six dependency and delete bmemcached/compat.py #266.
  7. Modernize the tests-and-lint workflow and add Renovate #271, Fix five defects: assert guards, exception message, password leak, circular import #273, and Harden test/conftest.py and close the IPv6 and SASL test gaps #274 are independent. Run them in parallel at any time.
  8. Gate publish.yml on tags and move PyPI to Trusted Publishing #272 needs manual PyPI and GitHub settings changes. Read the warning in that issue before you merge it.

Work that runs in parallel now

Three issues need no predecessor: #271, #273, and #274.

Verified facts

These claims come from direct checks, not from reading alone.

Out of scope

These items came up during the audits. Each one needs its own issue.

  • Tags v0.31.3, v0.31.4, and v0.32.0 have no GitHub release. The newest release is v0.31.2.
  • flake.nix:68 holds a manual copy of the version string. No automatic sync exists.
  • The test suite uses unittest.TestCase in eight files. A conversion to pytest style is a large, separate change.
  • docs/conf.py holds a stale intersphinx_mapping in the pre-Sphinx-1.3 format. It is a no-op on a modern Sphinx.

Activity

  1. jaysonsantos commented on Sep 8, 2026

    @jaysonsantos
    OwnerAuthor

    Seven pull requests are open for review. None is merged. No issue is closed by
    hand. Each pull request body holds a Closes #NNN line, which closes the issue
    on merge.

    The pull requests

    Pull request Base branch Closes Gate result
    #278 — set the Python 3.10 floor and remove the Python 2 support layer main #265, #266, #267 261 passed, flake8 0 errors
    #279 — migrate packaging to PEP 621 and replace flake8 with ruff #278 #268, #269, #270 261 passed, ruff check clean, ruff format --check clean
    #280 — modernize the tests-and-lint workflow and add Renovate #279 #271 261 passed, ruff check clean, ruff format --check clean
    #281 — gate publish.yml on tags and move PyPI to Trusted Publishing main #272 261 passed, flake8 0 errors
    #282 — replace assert guards, add an exception message, stop a password leak main #273 268 passed, flake8 0 errors
    #285 — harden the conftest fixtures and close the IPv6 and SASL gaps main #274 271 passed, flake8 0 errors
    #286 — remove the per-call struct format strings from the response parse path #278 #276 261 passed, flake8 0 errors

    The baseline on main is 261 passed. #282 adds 7 tests. #285 adds 10 tests.
    No pull request removes a test.

    Every gate ran in the Nix dev shell:

    nix develop --command bash -c 'pytest -q'
    

    Merge order

    #281, #282, and #285 are independent. Merge them in any order.

    #278 first, then #279, then #280. Each stacks on the one before, so a reader
    sees the incremental diff only. #286 also stacks on #278 and can merge as soon
    as #278 lands.

    Two pull requests need an action before merge

    #281 has a release hazard. It removes the PYPI_API_TOKEN password input.
    Register the trusted publisher on pypi.org and create the pypi environment in
    GitHub settings before you merge. A merge without those steps breaks the next
    tag push. The exact steps are at the top of the pull request body. I did not
    touch PyPI settings, GitHub environment settings, or the PYPI_API_TOKEN
    secret.

    #280 needs the Renovate GitHub App. renovate.json alone does nothing.
    Install the app at https://github.com/apps/renovate and grant it access to this
    repository. Two settings in that file need your preference: timezone is
    Europe/Berlin, and schedule runs once per week.

    Deferred

    #275 (py.typed and annotations) is not implemented. It depends on #278,
    #279, and #282, all of which are open. I left a comment on #275 that states
    this and records two facts that changed under it: the two # type: ignore
    comments it names are already gone, and the lone comment-style hint at
    bmemcached/client/mixin.py:153 now reads # type: (str, int) -> bool.

    Nothing was blocked

    Every issue in scope for this run is fully implemented. Two items came close.

    The SASL integration test in #285 needed a memcached built with SASL, which
    nixpkgs does not provide by default. #285 adds --enable-sasl to the
    flake.nix memcached override, in the same way the flake already adds
    --enable-tls, and adds cyrus_sasl.bin for saslpasswd2. The test runs and
    passes in the dev shell. It skips in CI, because .ci-before-script.sh
    installs neither sasl2-bin nor a SASL-enabled memcached. Adding those belongs
    to #271, not to #274, so I did not fold it in.

    The bytes() copy in _read_socket, the second item in #276, stays. #286
    holds the measurement and the reason: removing it changes public return types,
    because a bytearray slice is a bytearray. The acceptance criteria allow the
    copy to stay with a stated reason.

    Two new issues

    Both came out of #274 and are linked from #285. Neither is folded into a pull
    request.

    Notes on three decisions

    flake.nix was updated in the same pull request that removed each dependency,
    so no later gate runs against a stale dev shell. #278 drops six and mock.
    #279 drops flake8 and adds pkgs.ruff, plus twine so the packaging gate
    runs there. #285 adds SASL to the memcached build.

    Three pin versions differ from the issue text, because the issue text is now
    out of date. .pre-commit-config.yaml in #279 uses commitizen v4.18.0 and
    pre-commit-hooks v6.0.0 in place of the proposed v3.29.1 and v4.6.0.
    The ruff hook rev is v0.16.5, which matches the ruff version in the dev
    shell, so local runs and hook runs agree.

    actions/upload-artifact in #280 stays at v4, as the issue text specifies.
    The current release is v7.0.1. Renovate will propose the major bump once it
    runs.

    Three findings in the "Out of scope" section of this issue are untouched, as
    intended: the missing GitHub releases for v0.31.3, v0.31.4, and v0.32.0;
    the unittest.TestCase style across eight test files; and the stale
    intersphinx_mapping in docs/conf.py. The manual version copy at
    flake.nix:68 is also untouched, and #279 states this in its body.

    On the related report #250, which #274 asked me to check: #285 turns the most
    likely cause, a missing or slow memcached, into one clear skip message
    instead of a wall of ConnectionRefusedError. It does not confirm the original
    diagnosis, so I did not close that issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions