From b0199ae47be39903b533b39406dfcd837ccbe5d4 Mon Sep 17 00:00:00 2001 From: Daniel Bast <2790401+dbast@users.noreply.github.com> Date: Mon, 6 Mar 2023 16:24:01 +0100 Subject: [PATCH] Add+apply initial pre-commit config with isort (#116) * Add pre-commit with isort * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Jannis Leidel Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .flake8 | 2 ++ .github/workflows/tests.yml | 12 +++++----- .pre-commit-config.yaml | 25 ++++++++++++++++++++ cwp.py | 4 ++-- docs/source/conda.md | 4 ++-- docs/source/conf.py | 2 +- docs/source/defining-shortcuts.md | 4 ++-- docs/source/getting-started.md | 2 +- docs/source/robots.txt | 2 +- menuinst/__init__.py | 9 +++---- menuinst/_legacy/__init__.py | 7 +++--- menuinst/_legacy/main.py | 2 ++ menuinst/_legacy/utils.py | 1 - menuinst/_legacy/win32.py | 7 +++--- menuinst/_schema.py | 25 ++++++++++---------- menuinst/api.py | 10 ++++---- menuinst/data/menuinst.default.json | 2 +- menuinst/data/menuinst.schema.json | 6 ++--- menuinst/platforms/__init__.py | 12 ++++++---- menuinst/platforms/base.py | 14 +++++------ menuinst/platforms/linux.py | 9 ++++--- menuinst/platforms/osx.py | 14 +++++------ menuinst/platforms/win.py | 8 +++---- menuinst/platforms/win_utils/knownfolders.py | 2 +- menuinst/platforms/win_utils/registry.py | 10 ++++---- menuinst/platforms/win_utils/win_elevate.py | 22 +++++++++++++---- menuinst/utils.py | 15 ++++++------ news/116-pre-commit | 19 +++++++++++++++ pyproject.toml | 8 +++++-- setup.py | 1 + src/README.md | 4 ++-- src/resource.h | 2 +- tests/_legacy/test_menu_creation.py | 2 +- tests/_legacy/test_win32.py | 3 ++- tests/conftest.py | 8 +++---- tests/data/jsons/entitlements.json | 2 +- tests/data/jsons/example-3.invalid.json | 2 +- tests/data/jsons/no-platforms.json | 2 +- tests/data/jsons/osx_symlinks.json | 2 +- tests/data/jsons/precommands.json | 2 +- tests/data/jsons/sys-prefix.json | 2 +- tests/data/pkgs/package_1/menu.json | 2 +- tests/test_api.py | 11 ++++----- tests/test_conda.py | 14 +++++------ tests/test_data.py | 2 +- tests/test_schema.py | 6 ++--- tests/test_windows.py | 8 +++---- 47 files changed, 200 insertions(+), 134 deletions(-) create mode 100644 .flake8 create mode 100644 .pre-commit-config.yaml mode change 100644 => 100755 menuinst/platforms/win_utils/win_elevate.py create mode 100644 news/116-pre-commit diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..61d90815 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 99 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b2070300..b6f4f154 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -49,12 +49,12 @@ jobs: channels: conda-canary/label/conda-conda-pr-11882,conda-forge extra-specs: | python=${{ matrix.python-version }} - conda-canary/label/conda-conda-pr-11882::conda - pip - pytest - pytest-cov - pydantic - hypothesis + conda-canary/label/conda-conda-pr-11882::conda + pip + pytest + pytest-cov + pydantic + hypothesis hypothesis-jsonschema - shell: bash -el {0} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..39b0d8c8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,25 @@ +# disable autofixing PRs, commenting "pre-commit.ci autofix" on a pull request triggers a autofix +ci: + autofix_prs: false +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-added-large-files + - id: check-ast + - id: fix-byte-order-marker + - id: check-case-conflict + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: check-shebang-scripts-are-executable + - id: debug-statements + - id: detect-private-key + - id: mixed-line-ending + - id: end-of-file-fixer + - id: trailing-whitespace + - id: check-yaml + - id: check-merge-conflict + - repo: https://github.com/PyCQA/isort + rev: 5.12.0 + hooks: + - id: isort diff --git a/cwp.py b/cwp.py index 4ed77c93..c2d0a643 100644 --- a/cwp.py +++ b/cwp.py @@ -1,11 +1,11 @@ # this script is used on windows to wrap shortcuts so that they are executed within an environment # It only sets the appropriate prefix PATH entries - it does not actually activate environments +import argparse import os -import sys import subprocess +import sys from os.path import join, pathsep -import argparse from menuinst._legacy.knownfolders import FOLDERID, get_folder_path diff --git a/docs/source/conda.md b/docs/source/conda.md index 1dfee2db..2e243112 100644 --- a/docs/source/conda.md +++ b/docs/source/conda.md @@ -9,7 +9,7 @@ While it can be used as a Python library on its own, If you want to learn how to create shortcuts for `conda` packages you are building and maintaining, check {doc}`defining-shortcuts`. ``` -`menuinst` integrates natively with `conda`, so as an end user you don't need to do anything to enable it. +`menuinst` integrates natively with `conda`, so as an end user you don't need to do anything to enable it. If a package is shipping a `menuinst`-compatible shortcut, it will be detected at installation time, and `conda` will invoke `menuinst` on its own. If you want to change the default behavior, there are some command-line flags you can use: @@ -85,4 +85,4 @@ Use `sed` like this: ```bash $ sed "s/__PKG_VERSION__/${PKG_VERSION}/g" "${RECIPE_DIR}/menu.json" > "${PREFIX}/Menu/${PKG_NAME}_menu.json" ``` -```` \ No newline at end of file +```` diff --git a/docs/source/conf.py b/docs/source/conf.py index 88e7b8c1..411f3f45 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -5,8 +5,8 @@ # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html -from pathlib import Path import json +from pathlib import Path # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information diff --git a/docs/source/defining-shortcuts.md b/docs/source/defining-shortcuts.md index 875d6832..9316526e 100644 --- a/docs/source/defining-shortcuts.md +++ b/docs/source/defining-shortcuts.md @@ -19,7 +19,7 @@ If you want to learn more, check the {doc}`reference` for full details on the av The JSON configurations follow a well-defined schema documented at {ref}`schema`. ``` -## Minimal example +## Minimal example A minimal example to launch Python's `turtle` module would be: @@ -44,7 +44,7 @@ A minimal example to launch Python's `turtle` module would be: ```{tip} Note how the `menu_name` is using a placeholder `{{ PY_VER }}`. -`menuinst` supports Jinja-like variables. +`menuinst` supports Jinja-like variables. The full list of available placeholders is available at {ref}`placeholders`. ``` diff --git a/docs/source/getting-started.md b/docs/source/getting-started.md index 5edcfccd..a92f1bc6 100644 --- a/docs/source/getting-started.md +++ b/docs/source/getting-started.md @@ -1,7 +1,7 @@ # Getting started `menuinst` can be used to create shortcuts or menu items across operating systems. -It's designed to integrate nicely with `conda` packages, +It's designed to integrate nicely with `conda` packages, but it can be used without it to an extent via its Python API (see below). diff --git a/docs/source/robots.txt b/docs/source/robots.txt index 6a8a2dfc..b036f772 100644 --- a/docs/source/robots.txt +++ b/docs/source/robots.txt @@ -1,3 +1,3 @@ User-agent: * -Sitemap: https://conda.github.io/menuinst/sitemap.xml \ No newline at end of file +Sitemap: https://conda.github.io/menuinst/sitemap.xml diff --git a/menuinst/__init__.py b/menuinst/__init__.py index e116286f..096881e2 100644 --- a/menuinst/__init__.py +++ b/menuinst/__init__.py @@ -1,11 +1,11 @@ """ """ +import json import os import sys -import json -from os import PathLike from logging import getLogger as _getLogger +from os import PathLike try: from ._version import __version__ @@ -14,8 +14,9 @@ from ._legacy import install as _legacy_install -from .api import install as _api_install, remove as _api_remove -from .utils import DEFAULT_PREFIX, DEFAULT_BASE_PREFIX +from .api import install as _api_install +from .api import remove as _api_remove +from .utils import DEFAULT_BASE_PREFIX, DEFAULT_PREFIX _log = _getLogger(__name__) diff --git a/menuinst/_legacy/__init__.py b/menuinst/_legacy/__init__.py index 72bf5e17..7dba6214 100644 --- a/menuinst/_legacy/__init__.py +++ b/menuinst/_legacy/__init__.py @@ -3,9 +3,10 @@ # All rights reserved. from __future__ import absolute_import + +import json import logging import sys -import json from os.path import abspath, basename, exists, join try: @@ -13,11 +14,11 @@ except ImportError: __version__ = "dev" -from ..utils import DEFAULT_PREFIX, DEFAULT_BASE_PREFIX +from ..utils import DEFAULT_BASE_PREFIX, DEFAULT_PREFIX if sys.platform == 'win32': - from .win32 import Menu, ShortCut from ..platforms.win_utils.win_elevate import isUserAdmin, runAsAdmin + from .win32 import Menu, ShortCut def _install(path, remove=False, prefix=None, mode=None, root_prefix=None): diff --git a/menuinst/_legacy/main.py b/menuinst/_legacy/main.py index d34bc9ca..f5321f8f 100644 --- a/menuinst/_legacy/main.py +++ b/menuinst/_legacy/main.py @@ -2,8 +2,10 @@ from os.path import join import menuinst._legacy as menuinst + from ..utils import DEFAULT_PREFIX + def main(): from optparse import OptionParser diff --git a/menuinst/_legacy/utils.py b/menuinst/_legacy/utils.py index 7e09a644..eed4209a 100644 --- a/menuinst/_legacy/utils.py +++ b/menuinst/_legacy/utils.py @@ -3,7 +3,6 @@ from os.path import isdir, isfile, islink - def rm_empty_dir(path): try: os.rmdir(path) diff --git a/menuinst/_legacy/win32.py b/menuinst/_legacy/win32.py index d39c5260..152fb16c 100644 --- a/menuinst/_legacy/win32.py +++ b/menuinst/_legacy/win32.py @@ -5,17 +5,16 @@ from __future__ import absolute_import, unicode_literals import ctypes +import locale import logging import os -from os.path import isdir, join, exists import sys -import locale - +from os.path import exists, isdir, join -from .utils import rm_empty_dir, rm_rf from ..platforms.win_utils.knownfolders import dirs_src, folder_path from ..platforms.win_utils.winshortcut import create_shortcut from ..utils import DEFAULT_BASE_PREFIX +from .utils import rm_empty_dir, rm_rf # This allows debugging installer issues using DebugView from Microsoft. OutputDebugString = ctypes.windll.kernel32.OutputDebugStringW diff --git a/menuinst/_schema.py b/menuinst/_schema.py index cbab455f..74f596b3 100644 --- a/menuinst/_schema.py +++ b/menuinst/_schema.py @@ -2,13 +2,14 @@ Generate JSON schemas from pydantic models """ -from pprint import pprint -from typing import Optional, Union, List, Literal, Dict -from pathlib import Path -from logging import getLogger import json +from logging import getLogger +from pathlib import Path +from pprint import pprint +from typing import Dict, List, Literal, Optional, Union -from pydantic import BaseModel as _BaseModel, Field, constr, conlist +from pydantic import BaseModel as _BaseModel +from pydantic import Field, conlist, constr log = getLogger(__name__) @@ -75,8 +76,8 @@ class Windows(BasePlatformSpecific): class Linux(BasePlatformSpecific): """ - Linux-specific instructions. - + Linux-specific instructions. + Check the `Desktop entry specification `__ for more details. .. desktop-entry-spec: https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#recognized-keys @@ -86,7 +87,7 @@ class Linux(BasePlatformSpecific): """ Categories in which the entry should be shown in a menu. "See 'Registered categories' in the `Menu Spec `__. - + .. menu-spec: http://www.freedesktop.org/Standards/menu-spec """ DBusActivatable: Optional[bool] = None @@ -117,7 +118,7 @@ class Linux(BasePlatformSpecific): NoDisplay: Optional[bool] = None """ Do not show this item in the menu. Useful to associate MIME types - and other registrations, without having an actual clickable item. + and other registrations, without having an actual clickable item. Not to be confused with 'Hidden'. """ NotShowIn: Optional[Union[List[str], constr(regex=r"^.+;$")]] = None @@ -241,7 +242,7 @@ class CFBundleDocumentTypesModel(BaseModel): "Whether an app is prohibited from running simultaneously in multiple user sessions." LSRequiresNativeExecution: Optional[bool] = None """ - If true, prevent a universal binary from being run under + If true, prevent a universal binary from being run under Rosetta emulation on an Intel-based Mac. """ entitlements: Optional[List[constr(regex=r"[a-z0-9\.\-]+")]] = None @@ -261,8 +262,8 @@ class CFBundleDocumentTypesModel(BaseModel): class Platforms(BaseModel): """ - Platform specific options. - + Platform specific options. + Note each of these fields supports the same keys as the top-level :class:`MenuItem` (sans ``platforms`` itself), in case overrides are needed. """ diff --git a/menuinst/api.py b/menuinst/api.py index 15454527..7db04f40 100644 --- a/menuinst/api.py +++ b/menuinst/api.py @@ -1,16 +1,16 @@ """ """ -from os import PathLike +import json import sys -from typing import Union, List, Tuple, Literal, Optional, Callable, Any -from pathlib import Path import warnings -import json from logging import getLogger +from os import PathLike +from pathlib import Path +from typing import Any, Callable, List, Literal, Optional, Tuple, Union from .platforms import Menu, MenuItem -from .utils import elevate_as_needed, DEFAULT_PREFIX, DEFAULT_BASE_PREFIX +from .utils import DEFAULT_BASE_PREFIX, DEFAULT_PREFIX, elevate_as_needed log = getLogger(__name__) diff --git a/menuinst/data/menuinst.default.json b/menuinst/data/menuinst.default.json index 760cce46..b3e3d55b 100644 --- a/menuinst/data/menuinst.default.json +++ b/menuinst/data/menuinst.default.json @@ -58,4 +58,4 @@ } } ] -} \ No newline at end of file +} diff --git a/menuinst/data/menuinst.schema.json b/menuinst/data/menuinst.schema.json index eb33b72d..35a545b2 100644 --- a/menuinst/data/menuinst.schema.json +++ b/menuinst/data/menuinst.schema.json @@ -43,7 +43,7 @@ "definitions": { "Linux": { "title": "Linux", - "description": "Linux-specific instructions. \n\nCheck the `Desktop entry specification `__ for more details.\n\n.. desktop-entry-spec: https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#recognized-keys", + "description": "Linux-specific instructions.\n\nCheck the `Desktop entry specification `__ for more details.\n\n.. desktop-entry-spec: https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#recognized-keys", "type": "object", "properties": { "name": { @@ -517,7 +517,7 @@ }, "Platforms": { "title": "Platforms", - "description": "Platform specific options. \n\nNote each of these fields supports the same keys as the top-level :class:`MenuItem`\n(sans ``platforms`` itself), in case overrides are needed.", + "description": "Platform specific options.\n\nNote each of these fields supports the same keys as the top-level :class:`MenuItem`\n(sans ``platforms`` itself), in case overrides are needed.", "type": "object", "properties": { "linux": { @@ -597,4 +597,4 @@ "additionalProperties": false } } -} \ No newline at end of file +} diff --git a/menuinst/platforms/__init__.py b/menuinst/platforms/__init__.py index f561bce6..5282c854 100644 --- a/menuinst/platforms/__init__.py +++ b/menuinst/platforms/__init__.py @@ -1,18 +1,22 @@ import sys from typing import Tuple -from .base import Menu as BaseMenu, MenuItem as BaseMenuItem +from .base import Menu as BaseMenu +from .base import MenuItem as BaseMenuItem def menu_api_for_platform(platform: str = sys.platform) -> Tuple[BaseMenu, BaseMenuItem]: if platform == "win32": - from .win import WindowsMenu as Menu, WindowsMenuItem as MenuItem + from .win import WindowsMenu as Menu + from .win import WindowsMenuItem as MenuItem elif platform == "darwin": - from .osx import MacOSMenu as Menu, MacOSMenuItem as MenuItem + from .osx import MacOSMenu as Menu + from .osx import MacOSMenuItem as MenuItem elif platform.startswith("linux"): - from .linux import LinuxMenu as Menu, LinuxMenuItem as MenuItem + from .linux import LinuxMenu as Menu + from .linux import LinuxMenuItem as MenuItem else: raise ValueError(f"platform {platform} is not supported") diff --git a/menuinst/platforms/base.py b/menuinst/platforms/base.py index 4d03fae9..f67cfbbf 100644 --- a/menuinst/platforms/base.py +++ b/menuinst/platforms/base.py @@ -1,16 +1,16 @@ """ """ +import json import os import sys -from typing import Union, List, Iterable, Literal, Dict, Any, Optional, Mapping +from copy import deepcopy +from logging import getLogger from pathlib import Path from subprocess import check_output, run -from logging import getLogger -from copy import deepcopy from tempfile import NamedTemporaryFile -import json +from typing import Any, Dict, Iterable, List, Literal, Mapping, Optional, Union -from ..utils import slugify, data_path, deep_update, DEFAULT_PREFIX, DEFAULT_BASE_PREFIX +from ..utils import DEFAULT_BASE_PREFIX, DEFAULT_PREFIX, data_path, deep_update, slugify log = getLogger(__name__) @@ -140,13 +140,13 @@ def create(self) -> List[Path]: def remove(self) -> List[Path]: raise NotImplementedError - + @property def placeholders(self) -> Dict[str, str]: return { "MENU_ITEM_LOCATION": str(self.location), } - + def render_key(self, key: str, slug: bool = False, extra: Optional[Dict[str, str]] = None) -> Any: value = self.metadata.get(key) return self.render(value, slug=slug, extra=extra) diff --git a/menuinst/platforms/linux.py b/menuinst/platforms/linux.py index 6c86ccd8..66f337e4 100644 --- a/menuinst/platforms/linux.py +++ b/menuinst/platforms/linux.py @@ -1,16 +1,15 @@ """ """ import os -from pathlib import Path import shutil -import xml.etree.ElementTree as XMLTree import time +import xml.etree.ElementTree as XMLTree from logging import getLogger -from typing import Tuple, Iterable, Dict +from pathlib import Path +from typing import Dict, Iterable, Tuple +from ..utils import UnixLex, add_xml_child, indent_xml_tree, unlink from .base import Menu, MenuItem, menuitem_defaults -from ..utils import indent_xml_tree, add_xml_child, UnixLex, unlink - log = getLogger(__name__) diff --git a/menuinst/platforms/osx.py b/menuinst/platforms/osx.py index 642bb787..89d8e3c5 100644 --- a/menuinst/platforms/osx.py +++ b/menuinst/platforms/osx.py @@ -1,18 +1,18 @@ """ """ -from hashlib import sha1 -from logging import getLogger -from pathlib import Path -from subprocess import check_call -from typing import Tuple, Optional, Dict import os import platform import plistlib import shutil +from hashlib import sha1 +from logging import getLogger +from pathlib import Path +from subprocess import check_call +from typing import Dict, Optional, Tuple -from .base import Menu, MenuItem, menuitem_defaults -from ..utils import UnixLex from .. import data as _menuinst_data +from ..utils import UnixLex +from .base import Menu, MenuItem, menuitem_defaults log = getLogger(__name__) diff --git a/menuinst/platforms/win.py b/menuinst/platforms/win.py index 84c76287..963970c6 100644 --- a/menuinst/platforms/win.py +++ b/menuinst/platforms/win.py @@ -5,13 +5,12 @@ import warnings from logging import getLogger from pathlib import Path -from subprocess import run, CompletedProcess +from subprocess import CompletedProcess, run from tempfile import NamedTemporaryFile -from typing import Tuple, Optional, Dict, Any +from typing import Any, Dict, Optional, Tuple -from .base import Menu, MenuItem from ..utils import WinLex, unlink - +from .base import Menu, MenuItem from .win_utils.knownfolders import folder_path as windows_folder_path from .win_utils.registry import ( register_file_extension, @@ -385,4 +384,3 @@ def _unregister_url_protocols(self): for protocol in protocols: identifier = self._ftype_identifier(protocol) unregister_url_protocol(protocol, identifier, mode=self.parent.mode) - diff --git a/menuinst/platforms/win_utils/knownfolders.py b/menuinst/platforms/win_utils/knownfolders.py index 0b5157f5..2ece2028 100644 --- a/menuinst/platforms/win_utils/knownfolders.py +++ b/menuinst/platforms/win_utils/knownfolders.py @@ -28,8 +28,8 @@ import ctypes import os from ctypes import windll, wintypes -from uuid import UUID from logging import getLogger +from uuid import UUID logger = getLogger(__name__) diff --git a/menuinst/platforms/win_utils/registry.py b/menuinst/platforms/win_utils/registry.py index ba4f9fcb..ad1fff24 100644 --- a/menuinst/platforms/win_utils/registry.py +++ b/menuinst/platforms/win_utils/registry.py @@ -13,9 +13,9 @@ Mnemonic: SetValueEx for "excalars" (scalars, named values) """ +import winreg from logging import getLogger from subprocess import run -import winreg log = getLogger(__name__) @@ -47,7 +47,7 @@ def register_file_extension(extension, identifier, command, icon=None, mode="use DefaultIcon: "path to the app icon" shell/ open/ - command/: "the command to be executed when opening a file with this extension" + command/: "the command to be executed when opening a file with this extension" """ with winreg.OpenKeyEx( winreg.HKEY_LOCAL_MACHINE # HKLM @@ -58,9 +58,9 @@ def register_file_extension(extension, identifier, command, icon=None, mode="use # First we associate an extension with a handler winreg.SetValueEx( winreg.CreateKey(key, fr"{extension}\OpenWithProgids"), - identifier, + identifier, 0, - winreg.REG_SZ, + winreg.REG_SZ, "", # presence of the key is enough ) log.debug("Created registry entry for extension '%s'", extension) @@ -110,7 +110,7 @@ def register_url_protocol(protocol, command, identifier=None, icon=None, mode="u with key: winreg.SetValueEx(key, "", 0, winreg.REG_SZ, f"URL:{protocol.title()}") winreg.SetValueEx(key, "URL Protocol", 0, winreg.REG_SZ, "") - # SetValue creates sub keys when slashes are present; + # SetValue creates sub keys when slashes are present; # SetValueEx creates a value with backslashes - we don't want that here winreg.SetValue(key, r"shell\open\command", winreg.REG_SZ, command) if icon: diff --git a/menuinst/platforms/win_utils/win_elevate.py b/menuinst/platforms/win_utils/win_elevate.py old mode 100644 new mode 100755 index 2dea5789..2aea75cc --- a/menuinst/platforms/win_utils/win_elevate.py +++ b/menuinst/platforms/win_utils/win_elevate.py @@ -9,7 +9,10 @@ from __future__ import print_function -import sys, os, traceback + +import os +import sys +import traceback from enum import IntEnum from subprocess import list2cmdline @@ -24,6 +27,7 @@ def isUserAdmin(): raise RuntimeError("This function is only implemented on Windows.") import ctypes + # Requires Windows XP SP2 or higher! try: return ctypes.windll.shell32.IsUserAnAdmin() @@ -44,9 +48,19 @@ def ensure_binary(value): # In this case assume already binary type and do nothing return value - from ctypes import (POINTER, Structure, WinError, byref, c_ulong, c_char_p, c_int, - c_void_p, sizeof, windll) - from ctypes.wintypes import HANDLE, BOOL, DWORD, HWND, HINSTANCE, HKEY + from ctypes import ( + POINTER, + Structure, + WinError, + byref, + c_char_p, + c_int, + c_ulong, + c_void_p, + sizeof, + windll, + ) + from ctypes.wintypes import BOOL, DWORD, HANDLE, HINSTANCE, HKEY, HWND PHANDLE = POINTER(HANDLE) PDWORD = POINTER(DWORD) SEE_MASK_NOCLOSEPROCESS = 0x00000040 diff --git a/menuinst/utils.py b/menuinst/utils.py index 61721887..ecfd7dd8 100644 --- a/menuinst/utils.py +++ b/menuinst/utils.py @@ -9,9 +9,8 @@ from functools import wraps from logging import getLogger from pathlib import Path +from typing import Callable, Iterable, Literal, Mapping, Optional, Sequence, Union from unicodedata import normalize -from typing import Union, Literal, Optional, Sequence, Iterable, Mapping, Callable - logger = getLogger(__name__) _TargetOrBase = Union[Literal["target"], Literal["base"]] @@ -52,14 +51,14 @@ def _default_prefix(which: _TargetOrBase = "target"): if base: prefix = os.environ.get("MENUINST_BASE_PREFIX") if prefix: - return prefix + return prefix if context: return context.root_prefix return os.environ.get("CONDA_ROOT_PREFIX", sys.base_prefix) # else prefix = os.environ.get("MENUINST_PREFIX") if prefix: - return prefix + return prefix if context: return context.target_prefix return os.environ.get("CONDA_PREFIX", sys.prefix) @@ -244,7 +243,7 @@ def deep_update(mapping: Mapping, *updating_mappings: Iterable[Mapping]) -> Mapp def user_is_admin() -> bool: if os.name == 'nt': - from .platforms.win_utils.win_elevate import isUserAdmin + from .platforms.win_utils.win_elevate import isUserAdmin return isUserAdmin() elif os.name == 'posix': @@ -280,11 +279,11 @@ def python_executable(base_prefix: Optional[os.PathLike] = None) -> Sequence[str base_prefix_python = base_prefix / "python.exe" else: base_prefix_python = base_prefix / "bin" / "python" - # If the base env (installation root) + # If the base env (installation root) # ships a usable Python, use that one if base_prefix_python.is_file(): return (str(base_prefix_python), ) - # the base env does not have python, + # the base env does not have python, # use the conda-standalone wrapper return (sys.executable, "python") # in non-frozen executables: @@ -361,7 +360,7 @@ def wrapper_elevate( else: os.environ.pop("_MENUINST_RECURSING", None) if return_code == 0: # success, no need to fallback - return + return # We have not returned yet? Well, let's try as a normal user return func( base_prefix=base_prefix, diff --git a/news/116-pre-commit b/news/116-pre-commit new file mode 100644 index 00000000..977f1fdd --- /dev/null +++ b/news/116-pre-commit @@ -0,0 +1,19 @@ +### Enhancements + +* + +### Bug fixes + +* + +### Deprecations + +* + +### Docs + +* + +### Other + +* Enable and apply pre-commit with isort. (#116) diff --git a/pyproject.toml b/pyproject.toml index 69bac36d..8951da96 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,10 +22,10 @@ write_to = "menuinst/_version.py" include-package-data = true [tool.setuptools.packages.find] -where = ["."] +where = ["."] include = ["menuinst*"] namespaces = true - + [tool.black] line-length = 99 target-version = ['py38', 'py39', 'py310', 'py311'] @@ -39,5 +39,9 @@ exclude = ''' ) ''' +[tool.isort] +profile = "black" +line_length = 99 + [tool.pyright] pythonPlatform = "All" diff --git a/setup.py b/setup.py index d2dc0744..531c8adc 100644 --- a/setup.py +++ b/setup.py @@ -2,6 +2,7 @@ # Copyright (c) 2013 Continuum Analytics, Inc. # All rights reserved. import sys + from setuptools import Extension, setup extensions = [] diff --git a/src/README.md b/src/README.md index 2fc5d187..8ac4318b 100644 --- a/src/README.md +++ b/src/README.md @@ -7,7 +7,7 @@ Compilation happens via `setup.py`. # MacOS -`osx_launcher.c` will build a script launcher on MacOS. +`osx_launcher.c` will build a script launcher on MacOS. It will find a shell script next to itself (executable name + `-script`) and launch it with `/bin/sh`. It only depends on the standard library. It is bundled as part of the `menuinst.data`. If it changes, recompile using these steps: @@ -23,4 +23,4 @@ for target in "x86_64-apple-macos10.9" "arm64-apple-macos11.0"; do arch_suffix="${target%%-*}" clang src/osx_launcher.c -o menuinst/data/osx_launcher_${arch_suffix} -target $target -isysroot "$SDK_PATH" done -``` \ No newline at end of file +``` diff --git a/src/resource.h b/src/resource.h index 35ba3c25..1a4c0364 100644 --- a/src/resource.h +++ b/src/resource.h @@ -36,7 +36,7 @@ #define IDC_OTHERPYTHON 1026 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 112 diff --git a/tests/_legacy/test_menu_creation.py b/tests/_legacy/test_menu_creation.py index 7b72a000..2e9875f7 100644 --- a/tests/_legacy/test_menu_creation.py +++ b/tests/_legacy/test_menu_creation.py @@ -1,8 +1,8 @@ import os import sys -from conda.cli.python_api import run_command import pytest +from conda.cli.python_api import run_command if sys.platform == "win32": import menuinst._legacy as menuinst diff --git a/tests/_legacy/test_win32.py b/tests/_legacy/test_win32.py index db7cf896..f9ec138a 100644 --- a/tests/_legacy/test_win32.py +++ b/tests/_legacy/test_win32.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function, unicode_literals -from logging import getLogger + import sys +from logging import getLogger import pytest diff --git a/tests/conftest.py b/tests/conftest.py index e9fd401e..c05796f7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,8 @@ -from pathlib import Path -import shutil -import os import json import logging +import os +import shutil +from pathlib import Path from subprocess import check_output from tempfile import TemporaryDirectory @@ -53,7 +53,7 @@ def tmpdir(tmpdir, request): def mock_locations(monkeypatch, tmp_path): from menuinst.platforms.linux import LinuxMenu from menuinst.platforms.osx import MacOSMenuItem - + if os.name == "nt": from menuinst.platforms.win_utils import knownfolders diff --git a/tests/data/jsons/entitlements.json b/tests/data/jsons/entitlements.json index 1e9f7713..3edc7d1b 100644 --- a/tests/data/jsons/entitlements.json +++ b/tests/data/jsons/entitlements.json @@ -25,4 +25,4 @@ } } ] -} \ No newline at end of file +} diff --git a/tests/data/jsons/example-3.invalid.json b/tests/data/jsons/example-3.invalid.json index c674f47e..394734d0 100644 --- a/tests/data/jsons/example-3.invalid.json +++ b/tests/data/jsons/example-3.invalid.json @@ -17,4 +17,4 @@ } } ] -} \ No newline at end of file +} diff --git a/tests/data/jsons/no-platforms.json b/tests/data/jsons/no-platforms.json index 27ea2497..03e1fbee 100644 --- a/tests/data/jsons/no-platforms.json +++ b/tests/data/jsons/no-platforms.json @@ -15,4 +15,4 @@ "platforms": {} } ] -} \ No newline at end of file +} diff --git a/tests/data/jsons/osx_symlinks.json b/tests/data/jsons/osx_symlinks.json index b3f76d83..a8514356 100644 --- a/tests/data/jsons/osx_symlinks.json +++ b/tests/data/jsons/osx_symlinks.json @@ -21,4 +21,4 @@ } } ] -} \ No newline at end of file +} diff --git a/tests/data/jsons/precommands.json b/tests/data/jsons/precommands.json index a42d4331..3dad72d3 100644 --- a/tests/data/jsons/precommands.json +++ b/tests/data/jsons/precommands.json @@ -27,4 +27,4 @@ } } ] -} \ No newline at end of file +} diff --git a/tests/data/jsons/sys-prefix.json b/tests/data/jsons/sys-prefix.json index 2aa7ba81..aa7a8a6f 100644 --- a/tests/data/jsons/sys-prefix.json +++ b/tests/data/jsons/sys-prefix.json @@ -28,4 +28,4 @@ } } ] -} \ No newline at end of file +} diff --git a/tests/data/pkgs/package_1/menu.json b/tests/data/pkgs/package_1/menu.json index dc35140c..50bf4778 100644 --- a/tests/data/pkgs/package_1/menu.json +++ b/tests/data/pkgs/package_1/menu.json @@ -50,4 +50,4 @@ } } ] -} \ No newline at end of file +} diff --git a/tests/test_api.py b/tests/test_api.py index 57079401..35cede74 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,19 +1,18 @@ """""" import os import plistlib -import sys import subprocess +import sys from pathlib import Path from tempfile import NamedTemporaryFile from time import sleep, time import pytest +from conftest import DATA, PLATFORM from menuinst.api import install from menuinst.utils import DEFAULT_PREFIX -from conftest import DATA, PLATFORM - def check_output_from_shortcut(delete_files, json_path, expected_output=None): abs_json_path = DATA / "jsons" / json_path @@ -120,8 +119,8 @@ def test_no_entitlements_no_signature(delete_files): @pytest.mark.skipif(PLATFORM != "osx", reason="macOS only") def test_info_plist(delete_files): paths, _ = check_output_from_shortcut( - delete_files, - "entitlements.json", + delete_files, + "entitlements.json", expected_output="entitlements" ) app_dir = next(p for p in paths if p.name.endswith('.app')) @@ -139,7 +138,7 @@ def test_info_plist(delete_files): @pytest.mark.skipif(PLATFORM != "osx", reason="macOS only") def test_osx_symlinks(delete_files): paths, output = check_output_from_shortcut( - delete_files, + delete_files, "osx_symlinks.json", ) app_dir = next(p for p in paths if p.name.endswith('.app')) diff --git a/tests/test_conda.py b/tests/test_conda.py index ff9adbac..f062b6a1 100644 --- a/tests/test_conda.py +++ b/tests/test_conda.py @@ -1,23 +1,21 @@ """ Integration tests with conda """ +import json import os import sys +from contextlib import contextmanager +from pathlib import Path from subprocess import check_output from tempfile import NamedTemporaryFile -from pathlib import Path -from contextlib import contextmanager -import json import pytest from conda.models.version import VersionOrder -from conda.testing.integration import run_command +from conda.testing.integration import run_command +from conftest import BASE_PREFIX, DATA, PLATFORM -from menuinst.platforms import Menu, MenuItem from menuinst._schema import validate - -from conftest import DATA, PLATFORM, BASE_PREFIX - +from menuinst.platforms import Menu, MenuItem ENV_VARS = { k: v diff --git a/tests/test_data.py b/tests/test_data.py index fd174fba..ba8095d9 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -2,8 +2,8 @@ import json -from menuinst.utils import data_path from menuinst._schema import dump_default_to_json, dump_schema_to_json +from menuinst.utils import data_path def test_schema_is_up_to_date(): diff --git a/tests/test_schema.py b/tests/test_schema.py index 2e5c2b05..2718cbf7 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -1,11 +1,11 @@ import pytest +from conftest import DATA # from hypothesis import given, settings, HealthCheck # from hypothesis_jsonschema import from_schema from pydantic import ValidationError -from menuinst._schema import validate, MenuItem, BasePlatformSpecific -from conftest import DATA +from menuinst._schema import BasePlatformSpecific, MenuItem, validate # # suppress_health_check=3 --> too_slow # @settings(max_examples=100, suppress_health_check=[HealthCheck.too_slow]) @@ -29,7 +29,7 @@ def test_MenuItemMetadata_synced_with_OptionalMenuItemMetadata(): fields_as_optional = BasePlatformSpecific.__fields__ assert fields_as_required.keys() == fields_as_optional.keys() for (_, required), (_, optional) in zip( - sorted(fields_as_required.items()), + sorted(fields_as_required.items()), sorted(fields_as_optional.items()) ): assert required.field_info.description == optional.field_info.description diff --git a/tests/test_windows.py b/tests/test_windows.py index 71313fb3..c31df583 100644 --- a/tests/test_windows.py +++ b/tests/test_windows.py @@ -21,7 +21,7 @@ def test_file_extensions(tmp_path: Path, request): name = str(hash(str(tmp_path)))[:6] extension = f".menuinst-{name}" identifier = f"menuinst.assoc.menuinst-{name}" - + def cleanup(): # This key is not normally cleaned up because another programs might # be using it, but since we know these are synthetic and made up, @@ -30,7 +30,7 @@ def cleanup(): request.addfinalizer(cleanup) registry.register_file_extension( - extension=extension, + extension=extension, identifier=identifier, command=fr'cmd.exe /Q /D /V:ON /C "echo %1>{tmp_path}\output.txt"', mode="user", @@ -75,7 +75,7 @@ def test_protocols(tmp_path): """ name = str(hash(str(tmp_path)))[:6] registry.register_url_protocol( - protocol=f"menuinst-{name}", + protocol=f"menuinst-{name}", command=fr'cmd.exe /Q /D /V:ON /C "echo %1>{tmp_path}\output.txt"', identifier=f"menuinst.protocol.menuinst-{name}", mode="user", @@ -94,7 +94,7 @@ def test_protocols(tmp_path): raise AssertionError("Output file was never created") finally: registry.unregister_url_protocol( - protocol=f"menuinst-{name}", + protocol=f"menuinst-{name}", identifier=f"menuinst.protocol.menuinst-{name}", mode="user" )