crytic_compile.utils.naming

Module handling the file naming operation (relative -> absolute, etc)

  1"""
  2Module handling the file naming operation (relative -> absolute, etc)
  3"""
  4
  5import logging
  6import os.path
  7import platform
  8from dataclasses import dataclass
  9from pathlib import Path
 10from typing import TYPE_CHECKING, Union, Callable, Optional
 11
 12from crytic_compile.platform.exceptions import InvalidCompilation
 13
 14# Cycle dependency
 15if TYPE_CHECKING:
 16    from crytic_compile import CryticCompile
 17
 18LOGGER = logging.getLogger("CryticCompile")
 19
 20
 21@dataclass
 22class Filename:
 23    """Path metadata for each file in the compilation unit"""
 24
 25    def __init__(self, absolute: str, used: str, relative: str, short: str):
 26        self.absolute = absolute
 27        self.used = used
 28        self.relative = relative
 29        self.short = short
 30
 31    def __hash__(self) -> int:
 32        return hash(self.relative)
 33
 34    def __eq__(self, other: object) -> bool:
 35        if not isinstance(other, Filename):
 36            return NotImplemented
 37        return self.relative == other.relative
 38
 39    def __repr__(self) -> str:
 40        return f"Filename(absolute={self.absolute}, used={self.used}, relative={self.relative}, short={self.short}))"
 41
 42
 43def extract_name(name: str) -> str:
 44    """Convert '/path:Contract' to Contract
 45
 46    Args:
 47        name (str): name to convert
 48
 49    Returns:
 50        str: extracted contract name
 51    """
 52    return name[name.rfind(":") + 1 :]
 53
 54
 55def extract_filename(name: str) -> str:
 56    """Convert '/path:Contract' to /path
 57
 58    Args:
 59        name (str): name to convert
 60
 61    Returns:
 62        str: extracted filename
 63    """
 64    if not ":" in name:
 65        return name
 66    return name[: name.rfind(":")]
 67
 68
 69def combine_filename_name(filename: str, name: str) -> str:
 70    """Combine the filename with the contract name
 71
 72    Args:
 73        filename (str): filename
 74        name (str): contract name
 75
 76    Returns:
 77        str: Combined names
 78    """
 79    return filename + ":" + name
 80
 81
 82def _verify_filename_existence(filename: Path, cwd: Path) -> Path:
 83    """
 84    Check if the filename exist. If it does not, try multiple heuristics to find the right filename:
 85    - Look for contracts/FILENAME
 86    - Look for node_modules/FILENAME
 87    - Look for node_modules/FILENAME in all the parents directories
 88
 89
 90    Args:
 91        filename (Path): filename to check
 92        cwd (Path): directory
 93
 94    Raises:
 95        InvalidCompilation: if the filename is not found
 96
 97    Returns:
 98        Path: the filename
 99    """
100
101    if filename.exists():
102        return filename
103
104    if cwd.joinpath(Path("contracts"), filename).exists():
105        filename = cwd.joinpath("contracts", filename)
106    elif cwd.joinpath(filename).exists():
107        filename = cwd.joinpath(filename)
108    # how node.js loads dependencies from node_modules:
109    # https://nodejs.org/api/modules.html#loading-from-node_modules-folders
110    elif cwd.joinpath(Path("node_modules"), filename).exists():
111        filename = cwd.joinpath("node_modules", filename)
112    else:
113        for parent in cwd.parents:
114            if parent.joinpath(Path("node_modules"), filename).exists():
115                filename = parent.joinpath(Path("node_modules"), filename)
116                break
117
118    if not filename.exists():
119        raise InvalidCompilation(f"Unknown file: {filename}")
120
121    return filename
122
123
124# pylint: disable=too-many-branches
125def convert_filename(
126    used_filename: Union[str, Path],
127    relative_to_short: Callable[[Path], Path],
128    crytic_compile: "CryticCompile",
129    working_dir: Optional[Union[str, Path]] = None,
130) -> Filename:
131    """Convert a filename to CryticCompile Filename object.
132    The used_filename can be absolute, relative, or missing node_modules/contracts directory
133
134    Args:
135        used_filename (Union[str, Path]): Used filename
136        relative_to_short (Callable[[Path], Path]): Callback to translate the relative to short
137        crytic_compile (CryticCompile): Associated CryticCompile object
138        working_dir (Optional[Union[str, Path]], optional): Working directory. Defaults to None.
139
140    Returns:
141        Filename: Filename converted
142    """
143    filename_txt = used_filename
144    if platform.system() == "Windows":
145        elements = list(Path(filename_txt).parts)
146        if elements[0] == "/" or elements[0] == "\\":
147            elements = elements[1:]  # remove '/'
148            elements[0] = elements[0] + ":/"  # add :/
149        filename = Path(*elements)
150    else:
151        filename = Path(filename_txt)
152
153    # cwd points to the directory to be used
154    if working_dir is None:
155        cwd = Path.cwd()
156    else:
157        working_dir = Path(working_dir)
158        if working_dir.is_absolute():
159            cwd = working_dir
160        else:
161            cwd = Path.cwd().joinpath(Path(working_dir)).resolve()
162
163    if crytic_compile.package_name:
164        try:
165            filename = filename.relative_to(Path(crytic_compile.package_name))
166        except ValueError:
167            pass
168
169    filename = _verify_filename_existence(filename, cwd)
170
171    absolute = Path(os.path.abspath(filename))
172
173    # This returns original path if *path* and *start* are on different drives (for Windows platform).
174    try:
175        relative = Path(os.path.relpath(filename, Path.cwd()))
176    except ValueError:
177        relative = Path(filename)
178
179    # Build the short path
180    try:
181        if cwd.is_absolute():
182            short = absolute.relative_to(cwd)
183        else:
184            short = relative.relative_to(cwd)
185    except ValueError:
186        short = relative
187    except RuntimeError:
188        short = relative
189
190    short = relative_to_short(short)
191    # Starting with v0.8.8 (https://github.com/ethereum/solidity/pull/11545), solc normalizes the paths to not include the drive on Windows,
192    # so it's important we use posix path here to avoid issues with the path comparison.
193    return Filename(
194        absolute=absolute.as_posix(),
195        relative=relative.as_posix(),
196        short=short.as_posix(),
197        used=Path(used_filename).as_posix(),
198    )
LOGGER = <Logger CryticCompile (WARNING)>
class Filename:
23class Filename:
24    """Path metadata for each file in the compilation unit"""
25
26    def __init__(self, absolute: str, used: str, relative: str, short: str):
27        self.absolute = absolute
28        self.used = used
29        self.relative = relative
30        self.short = short
31
32    def __hash__(self) -> int:
33        return hash(self.relative)
34
35    def __eq__(self, other: object) -> bool:
36        if not isinstance(other, Filename):
37            return NotImplemented
38        return self.relative == other.relative
39
40    def __repr__(self) -> str:
41        return f"Filename(absolute={self.absolute}, used={self.used}, relative={self.relative}, short={self.short}))"

Path metadata for each file in the compilation unit

Filename(absolute: str, used: str, relative: str, short: str)
26    def __init__(self, absolute: str, used: str, relative: str, short: str):
27        self.absolute = absolute
28        self.used = used
29        self.relative = relative
30        self.short = short
absolute
used
relative
short
def extract_name(name: str) -> str:
44def extract_name(name: str) -> str:
45    """Convert '/path:Contract' to Contract
46
47    Args:
48        name (str): name to convert
49
50    Returns:
51        str: extracted contract name
52    """
53    return name[name.rfind(":") + 1 :]

Convert '/path:Contract' to Contract

Args: name (str): name to convert

Returns: str: extracted contract name

def extract_filename(name: str) -> str:
56def extract_filename(name: str) -> str:
57    """Convert '/path:Contract' to /path
58
59    Args:
60        name (str): name to convert
61
62    Returns:
63        str: extracted filename
64    """
65    if not ":" in name:
66        return name
67    return name[: name.rfind(":")]

Convert '/path:Contract' to /path

Args: name (str): name to convert

Returns: str: extracted filename

def combine_filename_name(filename: str, name: str) -> str:
70def combine_filename_name(filename: str, name: str) -> str:
71    """Combine the filename with the contract name
72
73    Args:
74        filename (str): filename
75        name (str): contract name
76
77    Returns:
78        str: Combined names
79    """
80    return filename + ":" + name

Combine the filename with the contract name

Args: filename (str): filename name (str): contract name

Returns: str: Combined names

def convert_filename( used_filename: Union[str, pathlib.Path], relative_to_short: Callable[[pathlib.Path], pathlib.Path], crytic_compile: crytic_compile.crytic_compile.CryticCompile, working_dir: Union[str, pathlib.Path, NoneType] = None) -> Filename:
126def convert_filename(
127    used_filename: Union[str, Path],
128    relative_to_short: Callable[[Path], Path],
129    crytic_compile: "CryticCompile",
130    working_dir: Optional[Union[str, Path]] = None,
131) -> Filename:
132    """Convert a filename to CryticCompile Filename object.
133    The used_filename can be absolute, relative, or missing node_modules/contracts directory
134
135    Args:
136        used_filename (Union[str, Path]): Used filename
137        relative_to_short (Callable[[Path], Path]): Callback to translate the relative to short
138        crytic_compile (CryticCompile): Associated CryticCompile object
139        working_dir (Optional[Union[str, Path]], optional): Working directory. Defaults to None.
140
141    Returns:
142        Filename: Filename converted
143    """
144    filename_txt = used_filename
145    if platform.system() == "Windows":
146        elements = list(Path(filename_txt).parts)
147        if elements[0] == "/" or elements[0] == "\\":
148            elements = elements[1:]  # remove '/'
149            elements[0] = elements[0] + ":/"  # add :/
150        filename = Path(*elements)
151    else:
152        filename = Path(filename_txt)
153
154    # cwd points to the directory to be used
155    if working_dir is None:
156        cwd = Path.cwd()
157    else:
158        working_dir = Path(working_dir)
159        if working_dir.is_absolute():
160            cwd = working_dir
161        else:
162            cwd = Path.cwd().joinpath(Path(working_dir)).resolve()
163
164    if crytic_compile.package_name:
165        try:
166            filename = filename.relative_to(Path(crytic_compile.package_name))
167        except ValueError:
168            pass
169
170    filename = _verify_filename_existence(filename, cwd)
171
172    absolute = Path(os.path.abspath(filename))
173
174    # This returns original path if *path* and *start* are on different drives (for Windows platform).
175    try:
176        relative = Path(os.path.relpath(filename, Path.cwd()))
177    except ValueError:
178        relative = Path(filename)
179
180    # Build the short path
181    try:
182        if cwd.is_absolute():
183            short = absolute.relative_to(cwd)
184        else:
185            short = relative.relative_to(cwd)
186    except ValueError:
187        short = relative
188    except RuntimeError:
189        short = relative
190
191    short = relative_to_short(short)
192    # Starting with v0.8.8 (https://github.com/ethereum/solidity/pull/11545), solc normalizes the paths to not include the drive on Windows,
193    # so it's important we use posix path here to avoid issues with the path comparison.
194    return Filename(
195        absolute=absolute.as_posix(),
196        relative=relative.as_posix(),
197        short=short.as_posix(),
198        used=Path(used_filename).as_posix(),
199    )

Convert a filename to CryticCompile Filename object. The used_filename can be absolute, relative, or missing node_modules/contracts directory

Args: used_filename (Union[str, Path]): Used filename relative_to_short (Callable[[Path], Path]): Callback to translate the relative to short crytic_compile (CryticCompile): Associated CryticCompile object working_dir (Optional[Union[str, Path]], optional): Working directory. Defaults to None.

Returns: Filename: Filename converted