diff --git a/.flake8 b/.flake8 index 97fffa9..f638660 100644 --- a/.flake8 +++ b/.flake8 @@ -29,6 +29,7 @@ exclude = # This contains builds of flake8 that we don't want to check dist, venv, - docs + docs, + maya_umbrella/_vendor max-line-length = 120 diff --git a/manual_test_in_maya.py b/manual_test_in_maya.py index f56b648..1976815 100644 --- a/manual_test_in_maya.py +++ b/manual_test_in_maya.py @@ -18,5 +18,8 @@ def get_virus_files(): def start(): for maya_file in get_virus_files(): - open_maya_file(maya_file) + try: + open_maya_file(maya_file) + except RuntimeError: + pass cmds.file(new=True, force=True) diff --git a/maya_umbrella/cleaner.py b/maya_umbrella/cleaner.py index c0edad4..cdae0cb 100644 --- a/maya_umbrella/cleaner.py +++ b/maya_umbrella/cleaner.py @@ -5,7 +5,7 @@ import re # Import local modules -from maya_umbrella.constants import FILE_VIRUS_SIGNATURES +from maya_umbrella.signatures import FILE_VIRUS_SIGNATURES from maya_umbrella.filesystem import remove_virus_file_by_signature from maya_umbrella.filesystem import safe_remove_file from maya_umbrella.filesystem import safe_rmtree diff --git a/maya_umbrella/constants.py b/maya_umbrella/constants.py index 6a6fab8..c6ed422 100644 --- a/maya_umbrella/constants.py +++ b/maya_umbrella/constants.py @@ -3,20 +3,3 @@ LOG_FORMAT = "%(asctime)s [%(levelname)s] %(name)s: %(message)s" LOG_MAX_BYTES = 1024 * 1024 * 5 - -FILE_VIRUS_SIGNATURES = [ - "import vaccine", - "cmds.evalDeferred.*leukocyte.+", - # https://regex101.com/r/0MNzF7/1 - "python(.*);.+exec.+(pyCode).+;", -] - -JOB_SCRIPTS_VIRUS_SIGNATURES = [ - "petri_dish_path.+cmds.internalVar.+", - "userSetup", - "fuckVirus", - # https://regex101.com/r/0MNzF7/1 - "python(.*);.+exec.+(pyCode).+;", - # https://regex101.com/r/2D14UA/1 - r"^\['.+']", -] diff --git a/maya_umbrella/filesystem.py b/maya_umbrella/filesystem.py index 1bb0ff8..5dee7d9 100644 --- a/maya_umbrella/filesystem.py +++ b/maya_umbrella/filesystem.py @@ -1,5 +1,4 @@ # Import built-in modules -import codecs from contextlib import contextmanager import glob import importlib @@ -10,16 +9,12 @@ import re import shutil import string -import sys import tempfile # Import local modules -from maya_umbrella.constants import FILE_VIRUS_SIGNATURES +from maya_umbrella.signatures import FILE_VIRUS_SIGNATURES from maya_umbrella.constants import PACKAGE_NAME - - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 +from maya_umbrella._vendor import six def this_root(): @@ -43,37 +38,24 @@ def safe_rmtree(path): pass -def _codes_open(path, encoding="utf-8"): - """Open and read the content of a file using the specified encoding. +def read_file(path): + """Read the file content. Args: - path (str): Path to the file. - encoding (str, optional): The encoding to use when reading the file. Defaults to "utf-8". + path (str): File path of source. Returns: - str: The content of the file, or an empty string if the file could not be read. - """ - try: - with codecs.open(path, "r", encoding) as file_: - return file_.read() - except (OSError, IOError): # noqa: UP024 - return "" + str: The contents of the file path. -def read_file(path): - """Read the content of the file at the given path.""" - options = {"encoding": "utf-8"} if PY3 else {} - with open(path, **options) as file_: - try: - content = file_.read() - # maya-2022 UnicodeDecodeError from `plug-ins/mayaHIK.pres.mel` - except UnicodeDecodeError: - return "" + """ + with open(path, "rb") as file_stream: + content = file_stream.read() return content def read_json(path): """Read the content of the file at the given path.""" - options = {"encoding": "utf-8"} if PY3 else {} + options = {"encoding": "utf-8"} if six.PY3 else {} with open(path, **options) as file_: try: content = json.load(file_) @@ -84,13 +66,12 @@ def read_json(path): def write_file(path, content): """Write the given content to the file at the given path.""" - options = {"encoding": "utf-8"} if PY3 else {} - with atomic_writes(path, "w", **options) as file_: - file_.write(content) + with atomic_writes(path, "wb") as file_: + file_.write(six.ensure_binary(content)) @contextmanager -def atomic_writes(src, mode, **options): +def atomic_writes(src, mode): """Context manager for atomic writes to a file. This context manager ensures that the file is only written to disk if the write operation completes without errors. @@ -107,15 +88,13 @@ def atomic_writes(src, mode, **options): AttributeError: If the os module does not have the 'replace' function (Python 2 compatibility). """ temp_path = os.path.join(os.path.dirname(src), "._{}".format(id_generator())) - open_func = open if PY3 else codecs.open - with open_func(temp_path, mode, **options) as f: + with open(temp_path, mode) as f: yield f try: os.replace(temp_path, src) except AttributeError: shutil.move(temp_path, src) - def id_generator(size=6, chars=string.ascii_uppercase + string.digits): """Generate a random string of the given size using the given characters.""" return "".join(random.choice(chars) for _ in range(size)) @@ -203,12 +182,7 @@ def remove_virus_file_by_signature(file_path, signatures, output_file_path=None, auto_remove: If True, remove the input file if the output file is empty. """ - try: - data = read_file(file_path) - except (OSError, IOError): # noqa: UP024 - return False - except UnicodeDecodeError: - data = _codes_open(file_path) + data = read_file(file_path) if check_virus_by_signature(data, signatures): fixed_data = replace_content_by_signatures(data, signatures).strip() if fixed_data: @@ -230,7 +204,7 @@ def replace_content_by_signatures(content, signatures): str: The cleaned content. """ for signature in signatures: - content = re.sub(signature, "", content) + content = re.sub(*map(six.ensure_binary, [signature, "", content])) return content @@ -250,7 +224,7 @@ def check_virus_file_by_signature(file_path, signatures=None): except (OSError, IOError): # noqa: UP024 return False except UnicodeDecodeError: - data = _codes_open(file_path) + data = "" return check_virus_by_signature(data, signatures) @@ -266,7 +240,7 @@ def check_virus_by_signature(content, signatures=None): """ signatures = signatures or FILE_VIRUS_SIGNATURES for signature in signatures: - if re.search(signature, content): + if re.search(*map(six.ensure_binary, [signature, content])): return True return False diff --git a/maya_umbrella/scanner.py b/maya_umbrella/scanner.py index 671d6b6..be57f56 100644 --- a/maya_umbrella/scanner.py +++ b/maya_umbrella/scanner.py @@ -12,7 +12,7 @@ from maya_umbrella.filesystem import get_backup_path from maya_umbrella.filesystem import read_file from maya_umbrella.maya_funs import cmds - +from maya_umbrella._vendor.six import PY2 class MayaVirusScanner(object): """A class to scan and fix Maya files containing viruses. @@ -55,8 +55,13 @@ def scan_files_from_pattern(self, pattern, glob_options=None): glob_options (dict): Optional keyword arguments for the glob module. if py3, we can pass a dict with the keyword arguments. {"recursive": True} + Raises: + ValueError: If py2, recursive is not supported. + """ glob_options = glob_options or {} + if "recursive" in glob_options and PY2: + raise ValueError("recursive is not supported in python2") os.environ.update(self._env) return self.scan_files_from_list(glob.iglob(pattern, **glob_options)) diff --git a/maya_umbrella/signatures.py b/maya_umbrella/signatures.py index 1918745..169b2f4 100644 --- a/maya_umbrella/signatures.py +++ b/maya_umbrella/signatures.py @@ -1,10 +1,23 @@ # Import built-in modules from collections import namedtuple - VirusSignature = namedtuple("VirusSignature", ["name", "signature"]) # https://regex101.com/r/0MNzF7/1 virus20240430_sig1 = VirusSignature("virus20240430", "python(.*);.+exec.+(pyCode).+;") # https://regex101.com/r/2D14UA/1 virus20240430_sig2 = VirusSignature("virus20240430", r"^\['.+']") + +JOB_SCRIPTS_VIRUS_SIGNATURES = [ + "petri_dish_path.+cmds.internalVar.+", + "userSetup", + "fuckVirus", + virus20240430_sig1.signature, + virus20240430_sig2.signature, +] + +FILE_VIRUS_SIGNATURES = [ + "import vaccine", + "cmds.evalDeferred.*leukocyte.+", + virus20240430_sig1.signature, +] diff --git a/maya_umbrella/vaccines/vaccine2.py b/maya_umbrella/vaccines/vaccine2.py index 24aae39..cb3164c 100644 --- a/maya_umbrella/vaccines/vaccine2.py +++ b/maya_umbrella/vaccines/vaccine2.py @@ -2,7 +2,7 @@ import os # Import local modules -from maya_umbrella.constants import JOB_SCRIPTS_VIRUS_SIGNATURES +from maya_umbrella.signatures import JOB_SCRIPTS_VIRUS_SIGNATURES from maya_umbrella.filesystem import check_virus_by_signature from maya_umbrella.filesystem import check_virus_file_by_signature from maya_umbrella.maya_funs import check_reference_node_exists diff --git a/maya_umbrella/vaccines/vaccine3.py b/maya_umbrella/vaccines/vaccine3.py index 559b9c3..2201d7e 100644 --- a/maya_umbrella/vaccines/vaccine3.py +++ b/maya_umbrella/vaccines/vaccine3.py @@ -3,7 +3,7 @@ import os # Import local modules -from maya_umbrella.constants import JOB_SCRIPTS_VIRUS_SIGNATURES +from maya_umbrella.signatures import JOB_SCRIPTS_VIRUS_SIGNATURES from maya_umbrella.filesystem import check_virus_by_signature from maya_umbrella.filesystem import check_virus_file_by_signature from maya_umbrella.maya_funs import cmds diff --git a/nox_actions/release.py b/nox_actions/release.py index 4339d98..1d70046 100644 --- a/nox_actions/release.py +++ b/nox_actions/release.py @@ -102,13 +102,3 @@ def pinned_requirements(path: Path) -> Iterator[Tuple[str, str]]: # synchronize the contents session.run("vendoring", "sync", ".") - - # Determine the correct message - message = f"Upgrade {name} to {new_version}" - - # Write our news fragment - news_file = Path("news") / (name + ".vendor.rst") - news_file.write_text(message + "\n") # "\n" appeases end-of-line-fixer - - # Commit the changes - # release.commit_file(session, ".", message=message) diff --git a/noxfile.py b/noxfile.py index 82e458a..bfb1eb2 100644 --- a/noxfile.py +++ b/noxfile.py @@ -25,4 +25,4 @@ nox.session(lint.lint_fix, name="lint-fix") nox.session(release.make_install_zip, name="make-zip") nox.session(codetest.pytest, name="pytest") -nox.session(release.vendoring) +nox.session(release.vendoring, name="vendoring") diff --git a/tests/test_filesystem.py b/tests/test_filesystem.py index 85ebff7..d1b0e9f 100644 --- a/tests/test_filesystem.py +++ b/tests/test_filesystem.py @@ -2,7 +2,7 @@ import pytest # Import local modules -from maya_umbrella.constants import FILE_VIRUS_SIGNATURES +from maya_umbrella.signatures import FILE_VIRUS_SIGNATURES from maya_umbrella.filesystem import check_virus_file_by_signature from maya_umbrella.filesystem import get_backup_path from maya_umbrella.filesystem import get_maya_install_root diff --git a/tests/test_scanner.py b/tests/test_scanner.py index ed1bd18..01675fd 100644 --- a/tests/test_scanner.py +++ b/tests/test_scanner.py @@ -7,14 +7,14 @@ from maya_umbrella.scanner import MayaVirusScanner -def test_scan_files_from_pattern(this_root): - scanner = MayaVirusScanner() +def test_scan_files_from_pattern(this_root, tmpdir): + scanner = MayaVirusScanner(output_path=str(tmpdir.join("test"))) root = os.path.join(this_root, "virus") assert scanner.scan_files_from_pattern(os.path.join(root, "*.m[ab]")) == [] def test_scanner_from_file(this_root, tmpdir): - scanner = MayaVirusScanner() + scanner = MayaVirusScanner(output_path=str(tmpdir.join("test"))) root = os.path.join(this_root, "virus") text_file = str(tmpdir.join("test.txt")) write_file(text_file, "\n".join(glob.glob(os.path.join(root, "*.m[ab]"))))