pythonwindowsattributes

WindowsDowndate tool error: AttributeError: type object 'PathEx' has no attribute '_from_parts'


I've been trying to use a python tool called WindowsDowndate from github (for educational purposes, I've been given the task to demonstrate some Windows exploit) but running the code just does not work.

Windows_downdate.py imports numerous methods from another file called filesystem_utils.py, where the error takes place:

from windows_downdate.filesystem_utils import PathEx, is_file_contents_equal, is_path_exists, read_file, write_file 

The PathEx class is defined in filesystem_utils as follows:

import filecmp
import os
from typing import Union, List, Any, TypeVar, Type, Self
from pathlib import WindowsPath, _from_parts


class PathEx(WindowsPath):
    """
    Extended WindowsPath class that supports NT path
    """

    TPathEx = TypeVar("TPathEx")

    def __new__(cls: Type[TPathEx], path: str, *args: Any, **kwargs: Any) -> TPathEx:
        """
        Create a new instance of PathEx, expanding environment variables in the provided path

        :param path: The initial path
        :param args: Additional positional arguments
        :param kwargs: Additional keyword arguments
        :return: A new instance of PathEx
        """
        expanded_path = os.path.expandvars(path)
        args = (expanded_path, ) + args
        self = cls._from_parts(args)
        return self

    @property
    def nt_path(self: Self) -> str:
        """
        :return: The NT path as string
        """
        return f"\\??\\{self.full_path}"

    @property
    def full_path(self: Self) -> str:
        """
        :return: The full path as string
        """
        return str(self)

I am a very amateur programmer so please forgive my ignorance, I don't quite know how to handle someone else's code.

Repository link: WindowsDowndate


Solution

  • I checked code in filesystem_utils.py and it has from pathlib import WindowsPath without _from_parts - maybe you use some old version.


    But there can be different problem.
    FULL error message/trackback should confirm it.

    Source code for pathlib in Python 3.11 shows that there was _from_parts in class PurePath but source code for Python 3.12 and Python 3.13 doesn't have it

    If you use Python 3.11 then it should work. Eventually you need only to remove _from_parts from line from pathlib import WindowsPath, _from_parts (if you really have _from_parts in this line) and keep only from pathlib import WindowsPath and it should resolve this problem.

    BTW: on repo you can even see (tested with python 3.11.9)

    If you use Python 3.12/3.13 then you may copy this function from 3.11 and put in your PathEx - but this also needs to copy other missing functions like _parse_args, etc. and maybe it would be simpler to copy full module pathlib for this

    class PathEx(WindowsPath):
    
        @classmethod
        def _from_parts(cls, args):
            # We need to call _parse_args on the instance, so as to get the
            # right flavour.
            self = object.__new__(cls)
            drv, root, parts = self._parse_args(args)
            self._drv = drv
            self._root = root
            self._parts = parts
            return self
    
        @classmethod
        def _parse_args(cls, args):
            # This is useful when you don't want to create an instance, just
            # canonicalize some constructor arguments.
            parts = []
            for a in args:
                if isinstance(a, PurePath):
                    parts += a._parts
                else:
                    a = os.fspath(a)
                    if isinstance(a, str):
                        # Force-cast str subclasses to str (issue #21127)
                        parts.append(str(a))
                    else:
                        raise TypeError(
                            "argument should be a str object or an os.PathLike "
                            "object returning str, not %r"
                            % type(a))
            return cls._flavour.parse_parts(parts)
    

    Eventually you should check if other function in 3.12/3.13 is doing the same and use it instead of _from_parts

    Of course it also needs to remove _from_parts from line from pathlib import WindowsPath