Skip to content

Commit

Permalink
Merge pull request #61 from ariebovenberg/next-release
Browse files Browse the repository at this point in the history
Next release
  • Loading branch information
ariebovenberg authored Feb 12, 2022
2 parents 1914f3e + d780545 commit 16e3dc6
Show file tree
Hide file tree
Showing 12 changed files with 55 additions and 23 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

0.12.1 (2022-02-12)
-------------------

- Add helpful link to 'failed to find module' message.

0.12.0 (2022-02-06)
-------------------

Expand Down
2 changes: 1 addition & 1 deletion docs/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Use the following configuration:
repos:
- repo: https://github.com/ariebovenberg/slotscheck
rev: v0.12.0
rev: v0.12.1
hooks:
- id: slotscheck
# If your Python files are not importable from the project root,
Expand Down
16 changes: 8 additions & 8 deletions docs/discovery.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
Module discovery
================

To check files, slotscheck needs to import them.
The process if importing files usually behaves as you would expect.
However, there are some complications that you may
need to be aware of.
Slotscheck needs to import files in order to check them.
This process usually behaves as you would expect.
However, there are some complications that you may need to be aware of.

.. admonition:: Summary

Expand All @@ -17,17 +16,18 @@ need to be aware of.

Whether you run ``python -m slotscheck`` or just ``slotscheck`` has an impact
on which files will be imported and checked.
This is not a choice by ``slotscheck``, but simply the way entry points work
in Python. When running ``python -m slotscheck``, the current working
This is not a choice by ``slotscheck``, but simply the way Python works.
When running ``python -m slotscheck``, the current working
directory is added to ``sys.path``, so any modules in the current directory
can be imported. This is not the case when running just ``slotscheck``.
can be imported. This is not the case when running bare ``slotscheck``.

So if you run ``slotscheck foo.py``, ``foo`` will not be importable.
In fact, if ``foo`` happens to be the name of an installed module,
``import foo`` will import that instead!
In that case ``slotscheck`` will refuse to run,
and print an informative message.
An alternative way to ensure the correct files can be imported is with the
``PYTHONPATH`` environment variable.
``$PYTHONPATH`` environment variable.

To illustrate all this, imagine the following file tree::

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "slotscheck"
version = "0.12.0"
version = "0.12.1"
description = "Ensure your __slots__ are working properly."
authors = ["Arie Bovenberg <[email protected]>"]
license = "MIT"
Expand Down
31 changes: 26 additions & 5 deletions src/slotscheck/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from pathlib import Path
from textwrap import indent
from typing import (
Any,
Collection,
Iterable,
Iterator,
Expand Down Expand Up @@ -114,7 +113,7 @@
@click.option(
"--strict-imports/--no-strict-imports",
help="Treat failed imports as errors.",
default=True,
default=None,
show_default="strict",
)
@click.option(
Expand All @@ -134,18 +133,40 @@ def root(
module: Sequence[str],
verbose: bool,
settings: Optional[AbsPath],
**kwargs: Any,
require_superclass: Optional[bool],
require_subclass: Optional[bool],
strict_imports: Optional[bool],
exclude_classes: Optional[config.RegexStr],
include_classes: Optional[config.RegexStr],
exclude_modules: Optional[config.RegexStr],
include_modules: Optional[config.RegexStr],
) -> None:
"Check whether your __slots__ are working properly."
conf = config.collect(kwargs, Path.cwd(), settings)
conf = config.collect(
config.PartialConfig(
strict_imports=strict_imports,
require_superclass=require_superclass,
require_subclass=require_subclass,
exclude_classes=exclude_classes,
include_classes=include_classes,
exclude_modules=exclude_modules,
include_modules=include_modules,
),
Path.cwd(),
settings,
)
if not (files or module):
print("No files or modules given. Nothing to do!")
exit(0)

try:
classes, modules = _collect(files, module, conf)
except ModuleNotFoundError as e:
print(_format_error(f"Module '{e.name}' not found."))
print(
f"ERROR: Module '{e.name}' not found.\n\n"
"See slotscheck.rtfd.io/en/latest/discovery.html\n"
"for help resolving common import problems."
)
exit(1)
except UnexpectedImportLocation as e:
print(
Expand Down
6 changes: 3 additions & 3 deletions src/slotscheck/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from dataclasses import dataclass, fields
from itertools import chain
from pathlib import Path
from typing import Any, ClassVar, Collection, Mapping, Optional, Type, TypeVar
from typing import ClassVar, Collection, Mapping, Optional, Type, TypeVar

import tomli

Expand Down Expand Up @@ -111,12 +111,12 @@ def apply(self, other: PartialConfig) -> "Config":


def collect(
cli_kwargs: Mapping[str, Any], cwd: Path, config: Optional[Path]
from_cli: PartialConfig, cwd: Path, config: Optional[Path]
) -> Config:
"Gather and combine configuration options from the available sources"
confpath = config or find_config_file(cwd)
conf = PartialConfig.load(confpath) if confpath else PartialConfig.EMPTY
return Config.DEFAULT.apply(conf).apply(PartialConfig(**cli_kwargs))
return Config.DEFAULT.apply(conf).apply(from_cli)


_CONFIG_FILENAMES = ("pyproject.toml", "setup.cfg")
Expand Down
Empty file.
Binary file added tests/examples/compiled/bar.pyc
Binary file not shown.
Empty file added tests/examples/compiled/foo.py
Empty file.
6 changes: 5 additions & 1 deletion tests/src/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ def test_no_inputs(runner: CliRunner):
def test_module_doesnt_exist(runner: CliRunner):
result = runner.invoke(cli, ["-m", "foo"])
assert result.exit_code == 1
assert result.output == "ERROR: Module 'foo' not found.\n"
assert result.output == (
"ERROR: Module 'foo' not found.\n\n"
"See slotscheck.rtfd.io/en/latest/discovery.html\n"
"for help resolving common import problems.\n"
)


def test_path_doesnt_exist(runner: CliRunner):
Expand Down
5 changes: 1 addition & 4 deletions tests/src/test_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import dataclasses
import re
from pathlib import Path

Expand Down Expand Up @@ -91,9 +90,7 @@ def test_parent_directory_cfg(self, tmpdir):
def test_collect(tmpdir):
(tmpdir / "setup.cfg").write_text(EXAMPLE_INI, encoding="utf-8")
(tmpdir / "pyproject.toml").write_binary(EXAMPLE_TOML)
assert collect(
dataclasses.asdict(Config.EMPTY), Path(tmpdir), None
).require_subclass
assert collect(PartialConfig.EMPTY, Path(tmpdir), None).require_subclass


class TestOptionsApply:
Expand Down
5 changes: 5 additions & 0 deletions tests/src/test_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ def test_unexpected_location(self):
EXAMPLES_DIR / "module_misc/a/b/c.py",
)

def test_pyc_file(self):
assert module_tree("compiled", None) == make_pkg(
"compiled", Module("foo"), Module("bar")
)


class TestFilterName:
def test_module(self):
Expand Down

0 comments on commit 16e3dc6

Please sign in to comment.