Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 36 additions & 25 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
exclude: >
(?x)^(
\.tox/.*
)$
repos:

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-docstring-first
- id: check-json
- id: check-yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.9.2
hooks:
# Run the linter.
- id: ruff
args: [ --fix, "--show-fixes"]
- id: ruff-format
types_or: [python]
- repo: https://github.com/codespell-project/codespell
rev: v2.4.0
hooks:
- id: codespell
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-merge-conflict
- id: check-yaml
- id: debug-statements
- id: end-of-file-fixer
- id: mixed-line-ending
- id: trailing-whitespace

- repo: https://github.com/psf/black
rev: 23.11.0
hooks:
- id: black
language_version: python3
args:
- --target-version=py38

- repo: https://github.com/MarcoGorelli/absolufy-imports
rev: v0.3.1
hooks:
- id: absolufy-imports

- repo: https://github.com/asottile/yesqa
rev: v1.5.0
hooks:
- id: yesqa

- repo: https://github.com/adamchainz/blacken-docs
rev: 1.16.0
hooks:
- id: blacken-docs
additional_dependencies:
- black
4 changes: 2 additions & 2 deletions docs/source/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ is display it, which is also the main functionality of the CLI, above.
Extra functionality in python code includes the ability to examine the internals
of a Project; here is a simple example, run in the projspec's repo root:

.. code-block:: python
.. code-block::

>>> import projspec
>>> proj = projspec.Project(".")
Expand All @@ -64,7 +64,7 @@ the answer is yes.

To execute an action on a project, one might do something like

.. code-block:: python
.. code-block::

>>> proj.uv.artifacts.wheel.make()

Expand Down
2 changes: 0 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ select = [
"PLC",
"PLE",
"PLR1722",
"PLR1736",
"PLW1501",
"PLW1510",
"PLW3301",
Expand All @@ -85,7 +84,6 @@ select = [
"PYI",
"RUF006",
"RUF015",
"RUF024",
"SIM",
"SLOT",
"SIM101",
Expand Down
4 changes: 2 additions & 2 deletions src/projspec/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ._version import __version__ # noqa: F401
from .proj import Project, ProjectSpec
from projspec._version import __version__ # noqa: F401
from projspec.proj import Project, ProjectSpec

__all__ = ["Project", "ProjectSpec"]
4 changes: 1 addition & 3 deletions src/projspec/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@
help='Type names to scan for (comma-separated list in camel or snake case); defaults to "ALL"',
)
@click.argument("path", default=".")
@click.option(
"--walk", is_flag=True, help="To descend into all child directories"
)
@click.option("--walk", is_flag=True, help="To descend into all child directories")
@click.option("--summary", is_flag=True, help="Show abbreviated output")
@click.option(
"--storage_options",
Expand Down
2 changes: 1 addition & 1 deletion src/projspec/artifact/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Things that a project can do or make"""

from .base import BaseArtifact, FileArtifact, get_artifact_cls
from projspec.artifact.base import BaseArtifact, FileArtifact, get_artifact_cls

__all__ = ["BaseArtifact", "FileArtifact", "get_artifact_cls"]
4 changes: 1 addition & 3 deletions src/projspec/artifact/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ def state(self) -> Literal["clean", "done", "pending"]:

def make(self, *args, **kwargs):
"""Create the artifact and any runtime it depends on"""
if not isinstance(
self.proj.fs, fsspec.implementations.local.LocalFileSystem
):
if not isinstance(self.proj.fs, fsspec.implementations.local.LocalFileSystem):
# Later, will implement download-and-make, although some tools
# can already do this themselves.
raise RuntimeError("Can't run local command on remote project")
Expand Down
2 changes: 1 addition & 1 deletion src/projspec/content/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .base import BaseContent, get_content_cls
from projspec.content.base import BaseContent, get_content_cls

__all__ = ["BaseContent", "get_content_cls"]
4 changes: 1 addition & 3 deletions src/projspec/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ def dict_to_html(data: dict, title="Data", open_level=2) -> str:
)
for i, item in enumerate(value):
if isinstance(item, dict):
html.append(
dict_to_html(item, f"{key}[{i}]", open_level - 1)
)
html.append(dict_to_html(item, f"{key}[{i}]", open_level - 1))
else:
html.append(f'<div style=" margin: 5px 0;"> {item}</div>')
html.append("</details>")
Expand Down
22 changes: 11 additions & 11 deletions src/projspec/proj/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from .base import ParseFailed, Project, ProjectSpec, get_projspec_class
from .conda_package import CondaRecipe, RattlerRecipe
from .conda_project import CondaProject
from .documentation import RTD, MDBook
from .git import GitRepo
from .pixi import Pixi
from .poetry import Poetry
from .pyscript import PyScript
from .python_code import PythonCode, PythonLibrary
from .rust import Rust, RustPython
from .uv import Uv
from projspec.proj.base import ParseFailed, Project, ProjectSpec, get_projspec_class
from projspec.proj.conda_package import CondaRecipe, RattlerRecipe
from projspec.proj.conda_project import CondaProject
from projspec.proj.documentation import RTD, MDBook
from projspec.proj.git import GitRepo
from projspec.proj.pixi import Pixi
from projspec.proj.poetry import Poetry
from projspec.proj.pyscript import PyScript
from projspec.proj.python_code import PythonCode, PythonLibrary
from projspec.proj.rust import Rust, RustPython
from projspec.proj.uv import Uv

__all__ = [
"get_projspec_class",
Expand Down
10 changes: 2 additions & 8 deletions src/projspec/proj/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,7 @@ def filter_by_type(self, types: Iterable[type]) -> bool:

def __contains__(self, item) -> bool:
"""Is the given project type supported ANYWHERE in this directory?"""
return item in self.specs or any(
item in _ for _ in self.children.values()
)
return item in self.specs or any(item in _ for _ in self.children.values())

def to_dict(self, compact=True) -> dict:
dic = AttrDict(
Expand Down Expand Up @@ -256,11 +254,7 @@ def __init__(self, proj: Project, subpath: str = ""):
@property
def path(self) -> str:
"""Location of this project spec"""
return (
self.proj.url + "/" + self.subpath
if self.subpath
else self.proj.url
)
return self.proj.url + "/" + self.subpath if self.subpath else self.proj.url

def match(self) -> bool:
"""Whether the given path might be interpreted as this type of project"""
Expand Down
18 changes: 4 additions & 14 deletions src/projspec/proj/conda_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ def parse(self) -> None:
from projspec.content.environment import Environment, Precision, Stack

if "recipe.yaml" in self.proj.basenames:
with self.proj.fs.open(
self.proj.basenames["recipe.yaml"], "rb"
) as f:
with self.proj.fs.open(self.proj.basenames["recipe.yaml"], "rb") as f:
meta = _yaml_no_jinja(f)
elif "meta.yaml" in self.proj.basenames:
with self.proj.fs.open(self.proj.basenames["meta.yaml"], "rb") as f:
Expand All @@ -93,16 +91,11 @@ def parse(self) -> None:
name = next(
filter(
bool,
(
meta.get(_, {}).get("name")
for _ in ("context", "recipe", "package")
),
(meta.get(_, {}).get("name") for _ in ("context", "recipe", "package")),
)
)

path = (
f"{self.proj.url}/output/{name}" if self.proj.is_local() else None
)
path = f"{self.proj.url}/output/{name}" if self.proj.is_local() else None
art = CondaPackage(
proj=self.proj,
cmd=cmd,
Expand All @@ -115,10 +108,7 @@ def parse(self) -> None:
req = next(
filter(
bool,
(
_.get("requirements")
for _ in [meta] + meta.get("outputs", [])
),
(_.get("requirements") for _ in [meta] + meta.get("outputs", [])),
)
)
self._contents = AttrDict(
Expand Down
6 changes: 1 addition & 5 deletions src/projspec/proj/conda_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,7 @@ def parse(self) -> None:
with self.proj.fs.open(f"{self.proj.url}/{fname}") as f:
env = _yaml_no_jinja(f)
channels.extend(
[
_
for _ in env.get("channels", [])
if _ not in channels
]
[_ for _ in env.get("channels", []) if _ not in channels]
)
packages.extend(
[
Expand Down
12 changes: 3 additions & 9 deletions src/projspec/proj/documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ class MDBook(ProjectSpec):
# to get generated docs output for a rust lib, use `rustdoc`
# https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html

spec_doc = (
"https://rust-lang.github.io/mdBook/format/configuration/index.html"
)
spec_doc = "https://rust-lang.github.io/mdBook/format/configuration/index.html"

def match(self) -> bool:
return "book.toml" in self.proj.basenames
Expand All @@ -30,14 +28,10 @@ class RTD(ProjectSpec):
General description of the platform: https://docs.readthedocs.com/platform/stable/
"""

spec_doc = (
"https://docs.readthedocs.com/platform/stable/config-file/v2.html"
)
spec_doc = "https://docs.readthedocs.com/platform/stable/config-file/v2.html"

def match(self) -> bool:
return any(
re.match("[.]?readthedocs.y[a]?ml", _) for _ in self.proj.basenames
)
return any(re.match("[.]?readthedocs.y[a]?ml", _) for _ in self.proj.basenames)

def parse(self) -> None:
# supports mkdocs and sphinx builders
Expand Down
12 changes: 3 additions & 9 deletions src/projspec/proj/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,14 @@ def parse(self) -> None:
cont = AttrDict()
cont["remotes"] = [
_.rsplit("/", 1)[-1]
for _ in self.proj.fs.ls(
f"{self.proj.url}/.git/refs/remotes", detail=False
)
for _ in self.proj.fs.ls(f"{self.proj.url}/.git/refs/remotes", detail=False)
]
cont["tags"] = [
_.rsplit("/", 1)[-1]
for _ in self.proj.fs.ls(
f"{self.proj.url}/.git/refs/tags", detail=False
)
for _ in self.proj.fs.ls(f"{self.proj.url}/.git/refs/tags", detail=False)
]
cont["branches"] = [
_.rsplit("/", 1)[-1]
for _ in self.proj.fs.ls(
f"{self.proj.url}/.git/refs/heads", detail=False
)
for _ in self.proj.fs.ls(f"{self.proj.url}/.git/refs/heads", detail=False)
]
self._contents = cont
19 changes: 5 additions & 14 deletions src/projspec/proj/pixi.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,9 @@ def parse(self) -> None:
meta = self.proj.pyproject.get("tools", {}).get("pixi", {})
if "pixi.toml" in self.proj.basenames:
try:
with self.proj.fs.open(
self.proj.basenames["pixi.toml"], "rb"
) as f:
with self.proj.fs.open(self.proj.basenames["pixi.toml"], "rb") as f:
meta.update(
toml.loads(
f.read().decode(), decoder=PickleableTomlDecoder()
)
toml.loads(f.read().decode(), decoder=PickleableTomlDecoder())
)
except (OSError, ValueError, UnicodeDecodeError, FileNotFoundError):
pass
Expand All @@ -97,15 +93,11 @@ def parse(self) -> None:
for env_name, details in meta["environments"].items():
feat = {}
feats = set(
details
if isinstance(details, list)
else details["features"]
details if isinstance(details, list) else details["features"]
)
for feat_name in feats:
feat.update(meta["feature"][feat_name])
if isinstance(details, list) or not details.get(
"no-default-feature"
):
if isinstance(details, list) or not details.get("no-default-feature"):
feat.update(meta)
extract_feature(feat, procs, commands, self, env=env_name)

Expand Down Expand Up @@ -229,8 +221,7 @@ def envs_from_lock(infile) -> dict:
for entry in next(iter(env["packages"].values()))
],
"channels": [
_ if isinstance(_, str) else _.get("url", "")
for _ in env["channels"]
_ if isinstance(_, str) else _.get("url", "") for _ in env["channels"]
]
+ env.get("indexes", []),
}
Expand Down
4 changes: 1 addition & 3 deletions src/projspec/proj/poetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ def parse(self) -> None:
fn=f"{self.proj.url}/poetry.lock",
)
try:
with self.proj.fs.open(
f"{self.proj.url}/poetry.lock", mode="rt"
) as f:
with self.proj.fs.open(f"{self.proj.url}/poetry.lock", mode="rt") as f:
pckg = toml.load(f, decoder=PickleableTomlDecoder())
packages = [
f"{_['name']} =={_['version']}" for _ in pckg.get("package", [])
Expand Down
4 changes: 1 addition & 3 deletions src/projspec/proj/pyscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ def match(self) -> bool:
# actually, config can be specified by a local path in the repo, but this is rare;
# also you can just declare things to install as you go, which we won't be able to
# guess.
return not {"pyscript.toml", "pyscript.json"}.isdisjoint(
self.proj.basenames
)
return not {"pyscript.toml", "pyscript.json"}.isdisjoint(self.proj.basenames)

def parse(self) -> None:
try:
Expand Down
Loading