Skip to content

Commit

Permalink
Update custom Path support for python 3.13 (#161)
Browse files Browse the repository at this point in the history
This pull request updates the project so that all tests pass with python
3.13. Changes include:

- ~Refactoring the `paths` module into a few submodules. This is a
source-compatible change that is not intended to introduce any
behavioural changes.~
- Adding in the new ~`databrickspath`~`_posixpath` submodule, so that
`PosixPath` can be treated as distinct from `DBFSPath` and
`WorkspacePath`.
 - Marking the project as supporting python 3.13.
 - Including python 3.13 in the build/CI version matrix.

~An incidental linting error was also fixed.~(Now on #167.)
  • Loading branch information
asnare authored Nov 8, 2024
1 parent 2b09928 commit 375182e
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
strategy:
fail-fast: false
matrix:
pyVersion: [ '3.10', '3.11', '3.12' ]
pyVersion: [ '3.10', '3.11', '3.12', '3.13' ]
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython",
]
dependencies = ["databricks-sdk>=0.16.0"]
Expand Down
9 changes: 9 additions & 0 deletions src/databricks/labs/blueprint/_posixpath.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""Databricks' path specification, in the same vein as :module:`posixpath` and :module:`ntpath`.
Paths are Posix-like, but we don't use the builtin :module:`posixpath` directly because as of Python 3.13 the module
itself is part of a path's identity for the purposes of comparison.
"""

from posixpath import join, sep

__all__ = ("join", "sep")
20 changes: 17 additions & 3 deletions src/databricks/labs/blueprint/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import locale
import logging
import os
import posixpath
import re
import shutil
import stat
Expand All @@ -30,6 +29,9 @@
ObjectType,
)

from databricks.labs.blueprint import _posixpath


logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -85,6 +87,19 @@ class _DatabricksPath(Path, abc.ABC): # pylint: disable=too-many-public-methods
# - Since 3.12 the implementation of these interfaces have been removed:
# 1. Flavour has been replaced with posixpath and ntpath (normally imported as os.path). Still class-scoped.
# 2. Accessor has been replaced with inline implementations based directly on the 'os' module.
# - Since 3.13 comparisons and equality include a check on the identity of the 'parser' property instead of using
# the flavour.
# - Comparisons for builtin paths use different strategies, depending on the python version.
# - Python 3.10, 3.11: Loose ("PurePath") type and flavour equality check, _cparts property check.
# - Python 3.12: Loose ("PurePath") type and flavour equality check, _str_normcase property check.
# - Python 3.13: Loose ("PurePath") type and parser identity check, _str_normcase property check.
# Although we can override comparisons when we are on the LHS, when a builtin path is on the LHS its comparison
# is first attempted. We deal with this with a combination of techniques:
# - Stubbing/emulating the internal properties that they use. (This means they don't trigger exceptions.)
# - Trying to force the builtin implementation to return NotImplemented: when this happens, Python will attempt
# the reversed comparison (by swapping LHS/RHS) and therefore our implementation is invoked.
# - Ensuring the parser property doesn't have the same identity as builtin parsers. (From python 3.13 only
# paths with the same parser object are comparable.)
#
# This implementation for Databricks-style paths does the following:
# 1. Flavour is basically posix-style, with the caveat that we don't bother with the special //-prefix handling.
Expand Down Expand Up @@ -113,8 +128,7 @@ class _DatabricksPath(Path, abc.ABC): # pylint: disable=too-many-public-methods
_str: str
_hash: int

# Path semantics are posix-like.
parser = posixpath
parser = _posixpath

# Compatibility attribute, for when superclass implementations get invoked on python <= 3.11.
_flavour = object()
Expand Down

0 comments on commit 375182e

Please sign in to comment.