Solution was quite easy. Thanks to the user reply here: beehaw.org/comment/3535588 or programming.dev/comment/10034690 (Not sure if the links actually work as expected…)
Hi all. I have a little problem and don’t know how to solve. A CLI program in Python is broken since Python 3.12. It was working in Python 3.11. The reason is, that Python 3.12 changed how subclassing of a pathlib.Path works (basically fixed an issue), which now breaks a workaround.
The class in question is:
class File(PosixPath):
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
return cls._from_parts(args).expanduser().resolve() # type: ignore
def __init__(self, source: str | Path, *args: Any) -> None:
super().__init__()
self.__source = Path(source)
@property
def source(self) -> Path:
return self.__source
@property
def modified(self) -> Time:
return Time.fromtimestamp(os.path.getmtime(self))
@property
def changed(self) -> Time:
return Time.fromtimestamp(os.path.getctime(self))
@property
def accessed(self) -> Time:
return Time.fromtimestamp(os.path.getatime(self))
# Calculate sha512 hash of self file and compare result to the
# checksum found in given file. Return True if identical.
def verify_sha512(self, file: File, buffer_size: int = 4096) -> bool:
compare_hash: str = file.read_text().split(" ")[0]
self_hash: str = ""
self_checksum = hashlib.sha512()
with open(self.as_posix(), "rb") as f:
for chunk in iter(lambda: f.read(buffer_size), b""):
self_checksum.update(chunk)
self_hash = self_checksum.hexdigest()
return self_hash == compare_hash
and I get this error when running the script:
Traceback (most recent call last):
File "/home/tuncay/.local/bin/geprotondl", line 1415, in
sys.exit(main())
^^^^^^
File "/home/tuncay/.local/bin/geprotondl", line 1334, in main
arguments, status = parse_arguments(argv)
^^^^^^^^^^^^^^^^^^^^^
File "/home/tuncay/.local/bin/geprotondl", line 1131, in parse_arguments
default, status = default_install_dir()
^^^^^^^^^^^^^^^^^^^^^
File "/home/tuncay/.local/bin/geprotondl", line 1101, in default_install_dir
steam_root: File = File(path)
^^^^^^^^^^
File "/home/tuncay/.local/bin/geprotondl", line 97, in __new__
return cls._from_parts(args).expanduser().resolve() # type: ignore
^^^^^^^^^^^^^^^
AttributeError: type object 'File' has no attribute '_from_parts'. Did you mean: '_load_parts'?
Now replacing _from_parts
with _load_parts
does not work either and I get this message in that case:
Traceback (most recent call last):
File "/home/tuncay/.local/bin/geprotondl", line 1415, in
sys.exit(main())
^^^^^^
File "/home/tuncay/.local/bin/geprotondl", line 1334, in main
arguments, status = parse_arguments(argv)
^^^^^^^^^^^^^^^^^^^^^
File "/home/tuncay/.local/bin/geprotondl", line 1131, in parse_arguments
default, status = default_install_dir()
^^^^^^^^^^^^^^^^^^^^^
File "/home/tuncay/.local/bin/geprotondl", line 1101, in default_install_dir
steam_root: File = File(path)
^^^^^^^^^^
File "/home/tuncay/.local/bin/geprotondl", line 97, in __new__
return cls._load_parts(args).expanduser().resolve() # type: ignore
^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/pathlib.py", line 408, in _load_parts
paths = self._raw_paths
^^^^^^^^^^^^^^^
AttributeError: 'tuple' object has no attribute '_raw_paths'
I have searched the web and don’t understand how to fix this. Has anyone an idea what to do?
Don’t really understand why you’re overriding
__new__
. Surely it’d work better to use:def __init__(self, source: str | Path, *args: Any) -> None: super().__init__(Path(source, *args).expanduser().resolve())
But this removes
self.__source
and the property. I’m not sure what the advantage of using that is but you could always set that before the linesuper().__init__(Path(source, *args).expanduser().resolve())
.EDIT: if I’ve completely misunderstood, please could you explain? I don’t really understand what subclassing is trying to achieve here, other than simplifying access to certain
os.path
functions.