Skip to content

Commit 4bb95a4

Browse files
authored
Merge pull request #3 from pomponchik/develop
0.0.3
2 parents 33d6f2c + 21a1365 commit 4bb95a4

File tree

10 files changed

+137
-22
lines changed

10 files changed

+137
-22
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
name: Bug report
3+
about: Create a report to help us improve
4+
title: ''
5+
labels: bug
6+
assignees: pomponchik
7+
8+
---
9+
10+
## Short description
11+
12+
Replace this text with a short description of the error and the behavior that you expected to see instead.
13+
14+
15+
## Describe the bug in detail
16+
17+
Please add this test in such a way that it reproduces the bug you found and does not pass:
18+
19+
```python
20+
def test_your_bug():
21+
...
22+
```
23+
24+
Writing the test, please keep compatibility with the [`pytest`](https://docs.pytest.org/) framework.
25+
26+
If for some reason you cannot describe the error in the test format, describe here the steps to reproduce it.
27+
28+
29+
## Environment
30+
- OS: ...
31+
- Python version (the output of the `python --version` command): ...
32+
- Version of this package: ...
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
name: Documentation fix
3+
about: Add something to the documentation, delete it, or change it
4+
title: ''
5+
labels: documentation
6+
assignees: pomponchik
7+
---
8+
9+
## It's cool that you're here!
10+
11+
Documentation is an important part of the project, we strive to make it high-quality and keep it up to date. Please adjust this template by outlining your proposal.
12+
13+
14+
## Type of action
15+
16+
What do you want to do: remove something, add it, or change it?
17+
18+
19+
## Where?
20+
21+
Specify which part of the documentation you want to make a change to? For example, the name of an existing documentation section or the line number in a file `README.md`.
22+
23+
24+
## The essence
25+
26+
Please describe the essence of the proposed change
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
name: Feature request
3+
about: Suggest an idea for this project
4+
title: ''
5+
labels: enhancement
6+
assignees: pomponchik
7+
8+
---
9+
10+
## Short description
11+
12+
What do you propose and why do you consider it important?
13+
14+
15+
## Some details
16+
17+
If you can, provide code examples that will show how your proposal will work. Also, if you can, indicate which alternatives to this behavior you have considered. And finally, how do you propose to test the correctness of the implementation of your idea, if at all possible?

.github/ISSUE_TEMPLATE/question.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
name: Question or consultation
3+
about: Ask anything about this project
4+
title: ''
5+
labels: guestion
6+
assignees: pomponchik
7+
8+
---
9+
10+
## Your question
11+
12+
Here you can freely describe your question about the project. Please, before doing this, read the documentation provided, and ask the question only if the necessary answer is not there. In addition, please keep in mind that this is a free non-commercial project and user support is optional for its author. The response time is not guaranteed in any way.

.github/workflows/tests_and_coverage.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
run: pip list
3333

3434
- name: Run tests and show coverage on the command line
35-
run: coverage run --source=suby --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m
35+
run: coverage run --source=suby --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=100
3636

3737
- name: Upload reports to codecov
3838
env:
@@ -45,4 +45,4 @@ jobs:
4545
./codecov -t ${CODECOV_TOKEN}
4646
4747
- name: Run tests and show the branch coverage on the command line
48-
run: coverage run --source=suby --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m
48+
run: coverage run --branch --source=suby --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=100

.github/workflows/tests_and_coverage_old.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
run: pip list
3333

3434
- name: Run tests and show coverage on the command line
35-
run: coverage run --source=suby --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m
35+
run: coverage run --source=suby --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=100
3636

3737
- name: Upload reports to codecov
3838
env:
@@ -45,4 +45,4 @@ jobs:
4545
./codecov -t ${CODECOV_TOKEN}
4646
4747
- name: Run tests and show the branch coverage on the command line
48-
run: coverage run --source=suby --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m
48+
run: coverage run --branch --source=suby --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=100

README.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,25 +52,33 @@ suby('python', '-c', 'print("hello, world!")')
5252

5353
## Run subprocess and look at the result
5454

55-
The `suby` function returns an object of the `SubprocessResult` class. It contains the following required fields:
55+
The `suby` module is a callable object and can be imported like this:
5656

57-
- **id** - a unique string that allows you to distinguish one result of calling the same command from another.
58-
- **stdout** - a string containing the entire buffered output of the command being run.
59-
- **stderr** - a string containing the entire buffered stderr of the command being run.
60-
- **returncode** - an integer indicating the return code of the subprocess. `0` means that the process was completed successfully, the other options usually indicate something bad.
61-
- **killed_by_token** - a boolean flag indicating whether the subprocess was killed due to [token](https://cantok.readthedocs.io/en/latest/the_pattern/) cancellation.
57+
```python
58+
import suby
59+
```
6260

63-
The simplest example of what it might look like:
61+
If you use static type checking and get an error that it is impossible to call the module, use a more detailed import form - functionally, these two import ways are identical:
6462

6563
```python
66-
import suby
64+
from suby import suby
65+
```
66+
67+
Let's try to call `suby`. You can use strings or [`pathlib.Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path) objects as positional arguments, but now we call it with only simple strings:
6768

69+
```python
6870
result = suby('python', '-c', 'print("hello, world!")')
6971
print(result)
7072
# > SubprocessResult(id='e9f2d29acb4011ee8957320319d7541c', stdout='hello, world!\n', stderr='', returncode=0, killed_by_token=False)
7173
```
7274

73-
You can use strings or [`pathlib.Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path) objects as positional arguments for `suby`.
75+
We can see that it returns an object of the `SubprocessResult` class. It contains the following required fields:
76+
77+
- **id** - a unique string that allows you to distinguish one result of calling the same command from another.
78+
- **stdout** - a string containing the entire buffered output of the command being run.
79+
- **stderr** - a string containing the entire buffered stderr of the command being run.
80+
- **returncode** - an integer indicating the return code of the subprocess. `0` means that the process was completed successfully, the other options usually indicate something bad.
81+
- **killed_by_token** - a boolean flag indicating whether the subprocess was killed due to [token](https://cantok.readthedocs.io/en/latest/the_pattern/) cancellation.
7482

7583

7684
## Output

pyproject.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "suby"
7-
version = "0.0.2"
7+
version = "0.0.3"
88
authors = [
99
{ name="Evgeniy Blinov", email="zheni-b@yandex.ru" },
1010
]
@@ -13,7 +13,7 @@ readme = "README.md"
1313
requires-python = ">=3.7"
1414
dependencies = [
1515
'emptylog>=0.0.7',
16-
'cantok>=0.0.18',
16+
'cantok>=0.0.24',
1717
]
1818
classifiers = [
1919
"Operating System :: OS Independent",
@@ -32,6 +32,11 @@ classifiers = [
3232
'Intended Audience :: Developers',
3333
'Topic :: Software Development :: Libraries',
3434
]
35+
keywords = [
36+
'subprocesses',
37+
'subprocesses wrapper',
38+
'execute commands',
39+
]
3540

3641
[tool.setuptools.package-data]
3742
"suby" = ["py.typed"]

suby/proxy_module.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
from subprocess import Popen, PIPE
55
from typing import List, Tuple, Callable, Union, Optional, Any
66
from pathlib import Path
7+
from types import ModuleType
78

89
from emptylog import EmptyLogger, LoggerProtocol
9-
from cantok import AbstractToken, TimeoutToken, CancellationError
10+
from cantok import AbstractToken, TimeoutToken, DefaultToken, CancellationError
1011

1112
from suby.errors import RunningCommandError
1213
from suby.subprocess_result import SubprocessResult
@@ -23,15 +24,15 @@ def __call__(
2324
stdout_callback: Callable[[str], Any] = stdout_with_flush,
2425
stderr_callback: Callable[[str], Any] = stderr_with_flush,
2526
timeout: Optional[Union[int, float]] = None,
26-
token: Optional[AbstractToken] = None,
27+
token: AbstractToken = DefaultToken(),
2728
) -> SubprocessResult:
2829
"""
2930
About reading from strout and stderr: https://stackoverflow.com/a/28319191/14522393
3031
"""
31-
if timeout is not None and token is None:
32+
if timeout is not None and isinstance(token, DefaultToken):
3233
token = TimeoutToken(timeout)
3334
elif timeout is not None:
34-
token += TimeoutToken(timeout) # type: ignore[operator]
35+
token += TimeoutToken(timeout)
3536

3637
converted_arguments = self.convert_arguments(arguments)
3738
arguments_string_representation = ' '.join([argument if ' ' not in argument else f'"{argument}"' for argument in converted_arguments])
@@ -44,7 +45,7 @@ def __call__(
4445

4546
with Popen(list(converted_arguments), stdout=PIPE, stderr=PIPE, bufsize=1, universal_newlines=True) as process:
4647
stderr_reading_thread = self.run_stderr_thread(process, stderr_buffer, result, catch_output, stderr_callback)
47-
if token is not None:
48+
if not isinstance(token, DefaultToken):
4849
killing_thread = self.run_killing_thread(process, token, result)
4950

5051
for line in process.stdout: # type: ignore[union-attr]
@@ -53,7 +54,7 @@ def __call__(
5354
stdout_callback(line)
5455

5556
stderr_reading_thread.join()
56-
if token is not None:
57+
if not isinstance(token, DefaultToken):
5758
killing_thread.join()
5859

5960
self.fill_result(stdout_buffer, stderr_buffer, process.returncode, result)
@@ -63,7 +64,7 @@ def __call__(
6364
if result.killed_by_token:
6465
logger.error(f'The execution of the "{arguments_string_representation}" command was canceled using a cancellation token.')
6566
try:
66-
token.check() # type: ignore[union-attr]
67+
token.check()
6768
except CancellationError as e:
6869
e.result = result # type: ignore[attr-defined]
6970
raise e
@@ -129,3 +130,7 @@ def fill_result(stdout_buffer: List[str], stderr_buffer: List[str], returncode:
129130
result.stdout = ''.join(stdout_buffer)
130131
result.stderr = ''.join(stderr_buffer)
131132
result.returncode = returncode
133+
134+
@property
135+
def suby(self) -> ModuleType:
136+
return self

tests/test_proxy_module.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,3 +331,13 @@ def test_use_path_object_as_first_positional_argument():
331331
assert result.stdout == 'kek\n'
332332
assert result.stderr == ''
333333
assert result.returncode == 0
334+
335+
336+
def test_suby_as_attribute_of_suby():
337+
assert suby.suby is suby
338+
339+
340+
def test_full_import_form():
341+
from suby import suby as local_suby
342+
343+
assert suby is local_suby

0 commit comments

Comments
 (0)