Skip to content

Commit

Permalink
Merge branch '2.1' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
bitprophet committed May 8, 2023
2 parents 1eb79f0 + 0fa82cf commit 63ad2bd
Show file tree
Hide file tree
Showing 13 changed files with 77 additions and 11 deletions.
7 changes: 7 additions & 0 deletions integration/_support/package/tasks/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from invoke import Collection

# Issue #934 (from #919) only seems to trigger on this style of 'from . import
# xxx' - a vanilla self-contained tasks/__init__.py is still fine!
from . import module

ns = Collection(module)
6 changes: 6 additions & 0 deletions integration/_support/package/tasks/module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from invoke import task


@task
def mytask(c):
print("hi!")
6 changes: 6 additions & 0 deletions integration/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ def bad_collection_exits_nonzero(self):
assert not result.stdout
assert result.stderr

@trap
def package_style_collections_internally_importable(self):
# After merging #919 blew this up and unit tests did not detect!
result = run("cd package && inv -l")
assert "mytask" in result.stdout

def loads_real_user_config(self):
path = os.path.expanduser("~/.invoke.yaml")
try:
Expand Down
2 changes: 1 addition & 1 deletion invoke/_version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version_info__ = (2, 1, 0)
__version_info__ = (2, 1, 1)
__version__ = ".".join(map(str, __version_info__))
1 change: 1 addition & 0 deletions invoke/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ def load(self, name: Optional[str] = None) -> Tuple[ModuleType, str]:
sys.path.insert(0, path)
# Actual import
module = module_from_spec(spec)
sys.modules[spec.name] = module # so 'from . import xxx' works
spec.loader.exec_module(module)
return module, os.path.dirname(spec.origin)
msg = "ImportError loading {!r}, raising ImportError"
Expand Down
8 changes: 7 additions & 1 deletion invoke/runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -1346,7 +1346,12 @@ def start(self, command: str, shell: str, env: Dict[str, Any]) -> None:

def kill(self) -> None:
pid = self.pid if self.using_pty else self.process.pid
os.kill(pid, signal.SIGKILL)
try:
os.kill(pid, signal.SIGKILL)
except ProcessLookupError:
# In odd situations where our subprocess is already dead, don't
# throw this upwards.
pass

@property
def process_is_finished(self) -> bool:
Expand Down Expand Up @@ -1385,6 +1390,7 @@ def returncode(self) -> Optional[int]:
return self.process.returncode

def stop(self) -> None:
super().stop()
# If we opened a PTY for child communications, make sure to close() it,
# otherwise long-running Invoke-using processes exhaust their file
# descriptors eventually.
Expand Down
9 changes: 9 additions & 0 deletions sites/www/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ Changelog
- :support:`936 backported` Make sure ``py.typed`` is in our packaging
manifest; without it, users working from a regular installation
can't perform type checks. Thanks to Nikita Sobolev for catch & patch.
- :release:`2.1.1 <2023-05-01>`
- :bug:`934` The `importlib` upgrade in 2.1 had a corner case bug (regarding
``from . import <submodule>`` functionality within package-like task trees)
which in turn exposed a false-pass in our test suite. Both have now been
fixed. Thanks to Greg Meyer and Robert J. Berger for the bug reports.
- :release:`2.0.1 <2023-04-29>`
- :bug:`910` Add more rigor around subprocess/runner shutdown to avoid spurious
exceptions & also fix downstream issues in libraries like Fabric. Reported by
Orlando Rodríguez.
- :release:`2.1.0 <2023-04-28>`
- :support:`675` Implement `importlib` and deprecate `imp` module. Patches
provided by Jesse P. Johnson
Expand Down
2 changes: 1 addition & 1 deletion tests/_support/namespacing.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from invoke import Collection, task, call

from package import module
from subspace import module


@task
Expand Down
5 changes: 5 additions & 0 deletions tests/_support/package/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from invoke import Collection

from . import module

ns = Collection(module)
5 changes: 5 additions & 0 deletions tests/_support/subspace/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from invoke import Collection

from . import module

ns = Collection(module)
6 changes: 6 additions & 0 deletions tests/_support/subspace/module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from invoke import task


@task
def mytask(c):
pass
11 changes: 8 additions & 3 deletions tests/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import sys
from importlib.util import spec_from_file_location
from types import ModuleType
from pathlib import Path

from pytest import raises

Expand Down Expand Up @@ -50,7 +51,7 @@ def adds_module_parent_dir_to_sys_path(self):
# Crummy doesn't-explode test.
_BasicLoader().load("namespacing")

def doesnt_dupliate_parent_dir_addition(self):
def doesnt_duplicate_parent_dir_addition(self):
_BasicLoader().load("namespacing")
_BasicLoader().load("namespacing")
# If the bug is present, this will be 2 at least (and often more, since
Expand All @@ -59,8 +60,12 @@ def doesnt_dupliate_parent_dir_addition(self):

def can_load_package(self):
loader = _BasicLoader()
# make sure it doesn't explode
loader.load("package")
# Load itself doesn't explode (tests 'from . import xxx' internally)
mod, loc = loader.load("package")
# Properties of returned values look as expected
package = Path(support) / "package"
assert loc == str(package)
assert mod.__file__ == str(package / "__init__.py")

def load_name_defaults_to_config_tasks_collection_name(self):
"load() name defaults to config.tasks.collection_name"
Expand Down
20 changes: 15 additions & 5 deletions tests/runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -1402,11 +1402,6 @@ def run_always_stops_timer(self):
runner.run(_)
runner._timer.cancel.assert_called_once_with()

def stop_cancels_timer(self):
runner = self._mocked_timer()
runner.stop()
runner._timer.cancel.assert_called_once_with()

def timer_aliveness_is_test_of_timing_out(self):
# Might be redundant, but easy enough to unit test
runner = Runner(Context())
Expand All @@ -1430,6 +1425,12 @@ def always_runs_no_matter_what(self):
runner.run(_)
runner.stop.assert_called_once_with()

def cancels_timer(self):
runner = self._runner()
runner._timer = Mock()
runner.stop()
runner._timer.cancel.assert_called_once_with()

class asynchronous:
def returns_Promise_immediately_and_finishes_on_join(self):
# Dummy subclass with controllable process_is_finished flag
Expand Down Expand Up @@ -1534,6 +1535,15 @@ def _run(self, *args, **kwargs):
def _runner(self, *args, **kwargs):
return _runner(*args, **dict(kwargs, klass=_FastLocal))

class stop:
@mock_subprocess()
def calls_super(self):
# Re #910
runner = self._runner()
runner._timer = Mock() # twiddled by parent class stop()
runner.run(_)
runner._timer.cancel.assert_called_once_with()

class pty:
@mock_pty()
def when_pty_True_we_use_pty_fork_and_os_exec(self):
Expand Down

0 comments on commit 63ad2bd

Please sign in to comment.