iripau 1.1.0__tar.gz → 1.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {iripau-1.1.0 → iripau-1.1.2}/PKG-INFO +1 -1
  2. iripau-1.1.2/iripau/executable.py +176 -0
  3. {iripau-1.1.0 → iripau-1.1.2}/iripau/logging.py +2 -2
  4. {iripau-1.1.0 → iripau-1.1.2}/iripau/requests.py +29 -1
  5. {iripau-1.1.0 → iripau-1.1.2}/iripau/subprocess.py +3 -3
  6. {iripau-1.1.0 → iripau-1.1.2}/iripau.egg-info/PKG-INFO +1 -1
  7. {iripau-1.1.0 → iripau-1.1.2}/pyproject.toml +1 -1
  8. {iripau-1.1.0 → iripau-1.1.2}/tests/test_requests.py +56 -0
  9. iripau-1.1.0/iripau/executable.py +0 -108
  10. {iripau-1.1.0 → iripau-1.1.2}/LICENSE +0 -0
  11. {iripau-1.1.0 → iripau-1.1.2}/README.md +0 -0
  12. {iripau-1.1.0 → iripau-1.1.2}/iripau/__init__.py +0 -0
  13. {iripau-1.1.0 → iripau-1.1.2}/iripau/functools.py +0 -0
  14. {iripau-1.1.0 → iripau-1.1.2}/iripau/random.py +0 -0
  15. {iripau-1.1.0 → iripau-1.1.2}/iripau/shutil.py +0 -0
  16. {iripau-1.1.0 → iripau-1.1.2}/iripau/threading.py +0 -0
  17. {iripau-1.1.0 → iripau-1.1.2}/iripau.egg-info/SOURCES.txt +0 -0
  18. {iripau-1.1.0 → iripau-1.1.2}/iripau.egg-info/dependency_links.txt +0 -0
  19. {iripau-1.1.0 → iripau-1.1.2}/iripau.egg-info/requires.txt +0 -0
  20. {iripau-1.1.0 → iripau-1.1.2}/iripau.egg-info/top_level.txt +0 -0
  21. {iripau-1.1.0 → iripau-1.1.2}/setup.cfg +0 -0
  22. {iripau-1.1.0 → iripau-1.1.2}/tests/test_command.py +0 -0
  23. {iripau-1.1.0 → iripau-1.1.2}/tests/test_executable.py +0 -0
  24. {iripau-1.1.0 → iripau-1.1.2}/tests/test_functools.py +0 -0
  25. {iripau-1.1.0 → iripau-1.1.2}/tests/test_logging.py +0 -0
  26. {iripau-1.1.0 → iripau-1.1.2}/tests/test_random.py +0 -0
  27. {iripau-1.1.0 → iripau-1.1.2}/tests/test_shutil.py +0 -0
  28. {iripau-1.1.0 → iripau-1.1.2}/tests/test_subprocess.py +0 -0
  29. {iripau-1.1.0 → iripau-1.1.2}/tests/test_threading.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iripau
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: Python utilities focused on command execution
5
5
  Author: Ricardo Quezada
6
6
  Maintainer: Ricardo Quezada
@@ -0,0 +1,176 @@
1
+ """
2
+ Execute commands as Python functions
3
+ """
4
+
5
+ from shlex import split
6
+ from random import choice
7
+ from itertools import chain
8
+
9
+ from iripau.command import host_run
10
+
11
+
12
+ class Command:
13
+ """ Run an executable command as a Python callable
14
+
15
+
16
+
17
+ """
18
+
19
+ def __init__(self, parent, command):
20
+ self._parent = parent
21
+ self._command = parent._mk_command(command)
22
+ self._mk_command = parent._mk_command
23
+
24
+ def __getattr__(self, command):
25
+ child = Command(self, command)
26
+ setattr(self, command, child)
27
+ return child
28
+
29
+ def __call__(self, *args, **kwargs):
30
+ return self._parent(self._command, *args, **kwargs)
31
+
32
+
33
+ def make_command(command):
34
+ """ Replace underscore with dash.
35
+
36
+ Suitable for a CLI that uses dashes as word-separator in their
37
+ positional arguments.
38
+
39
+ Args:
40
+ command (str): Python identifier referring to a CLI command or
41
+ sub-command.
42
+
43
+ Returns:
44
+ str: Final token to be used as a CLI positional argument.
45
+ """
46
+ return command.replace("_", "-")
47
+
48
+
49
+ def make_option(option):
50
+ """ Replace underscore with dash and prepend two more dashes.
51
+
52
+ Suitable for a CLI that uses dashes as word-separator in their
53
+ positional arguments.
54
+
55
+ Args:
56
+ option (str): Python identifier referring to a CLI optional
57
+ argument.
58
+
59
+ Returns:
60
+ Tuple[str]: The tokens that could be used as a CLI positional argument.
61
+ """
62
+ return "--" + option.replace("_", "-"),
63
+
64
+
65
+ class Executable:
66
+ """ Run an executable as a Python callable.
67
+
68
+ Args:
69
+ executable (str): Path to an executable file or just the name if it
70
+ exists in the ``PATH``.
71
+ make_command (Callable[[str], str]): Function to convert a Python
72
+ identifier to the corresponding command positional argument for
73
+ the CLI.
74
+ make_option (Callable[str], Tuple[str]): Function to convert a Python
75
+ identifier into the corresponding optional argument for the CLI.
76
+ randomly, and
77
+ alias (str, list(str)): Alias for ``executable``. See ``alias`` in
78
+ `iripau.subprocess.Popen`_.
79
+ run_args_prefix (str): When calling an instance of this class, all
80
+ of the ``**kwargs`` starting with this prefix will be passed to
81
+ the ``run_function`` after removing the prefix.
82
+ run_function (Callable[List[str] *Any], subprocess.CompletedProcess):
83
+ The function that will actually run the process, wait for it and
84
+ return a ``CompletedProcess``.
85
+
86
+ If ``None``, the default function is `iripau.command.host_run`_.
87
+ **kwargs: Keyword arguments to be passed to ``run_function`` every
88
+ time an instance of this object is called.
89
+ The ``run_args_prefix`` is not needed here.
90
+ """
91
+ def __init__(
92
+ self, executable, make_command=make_command, make_option=make_option,
93
+ alias=None, run_args_prefix="_", run_function=None, **kwargs
94
+ ):
95
+ self._run = run_function or host_run
96
+ self._exe = split(executable) if isinstance(executable, str) else executable
97
+ self._alias = split(alias) if isinstance(alias, str) else alias
98
+ self._kwargs = kwargs
99
+
100
+ self._prefix = run_args_prefix
101
+ self._mk_option = make_option
102
+ self._mk_command = make_command
103
+
104
+ def __getattr__(self, command):
105
+ child = Command(self, command)
106
+ setattr(self, command, child)
107
+ return child
108
+
109
+ def __call__(self, *args, **kwargs):
110
+ optionals = chain.from_iterable(
111
+ self._make_arg(self._mk_option(key), value)
112
+ for key, value in kwargs.items()
113
+ if not key.startswith(self._prefix)
114
+ )
115
+
116
+ positionals = list(map(str, args))
117
+ optionals = list(optionals)
118
+
119
+ kwargs = {
120
+ key[len(self._prefix):]: value
121
+ for key, value in kwargs.items()
122
+ if key.startswith(self._prefix)
123
+ }
124
+
125
+ if self._alias:
126
+ kwargs.setdefault("alias", self._alias + positionals + optionals)
127
+
128
+ cmd = self._exe + positionals + optionals
129
+ return self._run(cmd, **self._kwargs, **kwargs)
130
+
131
+ @staticmethod
132
+ def _is_iterable(value):
133
+ if isinstance(value, (str, bytes)):
134
+ return False
135
+ return hasattr(value, "__iter__")
136
+
137
+ @classmethod
138
+ def _make_arg(cls, options, value):
139
+ """ Return a list of tokens.
140
+
141
+ Args:
142
+ options (Tuple[str]): One of this strings will be randomly chosen.
143
+
144
+ If the chosen option starts with ``--``, there will be a
145
+ single token with the option and the value: ``["--option=value"]``
146
+
147
+ If not, there will be two tokens: ``["-o", "value"]``.
148
+ value: The value of the optional argument for the CLI.
149
+
150
+ If ``True``, the option will be treated as a flag, with no
151
+ value: ``["--option"]``.
152
+
153
+ If ``False`` or ``None``, the option will be ignored: ``[]``.
154
+
155
+ If an iterable and not a string, the option will be repeated
156
+ for each item: ``["--option=value1", "--option=value2"]
157
+
158
+ Otherwise, it will be converted to a string before using it.
159
+ """
160
+ if cls._is_iterable(value):
161
+ return chain.from_iterable(
162
+ cls._make_arg(options, item)
163
+ for item in value
164
+ )
165
+
166
+ if value in {None, False}:
167
+ return []
168
+
169
+ option = choice(options)
170
+ if value is True:
171
+ return [option]
172
+
173
+ value = str(value)
174
+ if option.startswith("--"):
175
+ return [option + "=" + value]
176
+ return [option, value]
@@ -50,8 +50,8 @@ class LoggerFile:
50
50
 
51
51
 
52
52
  class SimpleThreadNameFormatter(logging.Formatter):
53
- """ The same logging.Formatter but threadName is just the first token after
54
- splitting it: record.threadName.split(maxsplit=1)[0]
53
+ """ The same ``logging.Formatter`` but threadName is just the first token
54
+ after splitting it: ``record.threadName.split(maxsplit=1)[0]``.
55
55
  """
56
56
 
57
57
  def format(self, record):
@@ -1,5 +1,5 @@
1
1
  """
2
- Utilities for the requests module
2
+ A wrapper of the requests module
3
3
  """
4
4
 
5
5
  import requests
@@ -79,3 +79,31 @@ class Session(requests.Session):
79
79
  echo
80
80
  )
81
81
  return response
82
+
83
+
84
+ def delete(*args, **kwargs):
85
+ return Session().delete(*args, **kwargs)
86
+
87
+
88
+ def get(*args, **kwargs):
89
+ return Session().get(*args, **kwargs)
90
+
91
+
92
+ def head(*args, **kwargs):
93
+ return Session().head(*args, **kwargs)
94
+
95
+
96
+ def options(*args, **kwargs):
97
+ return Session().options(*args, **kwargs)
98
+
99
+
100
+ def patch(*args, **kwargs):
101
+ return Session().patch(*args, **kwargs)
102
+
103
+
104
+ def post(*args, **kwargs):
105
+ return Session().post(*args, **kwargs)
106
+
107
+
108
+ def put(*args, **kwargs):
109
+ return Session().put(*args, **kwargs)
@@ -459,17 +459,17 @@ def set_global_echo(value):
459
459
 
460
460
  def set_global_stdout_files(*files: TeeStream):
461
461
  global GLOBAL_STDOUTS
462
- GLOBAL_STDOUTS = set(*files)
462
+ GLOBAL_STDOUTS = set(files)
463
463
 
464
464
 
465
465
  def set_global_stderr_files(*files: TeeStream):
466
466
  global GLOBAL_STDERRS
467
- GLOBAL_STDERRS = set(*files)
467
+ GLOBAL_STDERRS = set(files)
468
468
 
469
469
 
470
470
  def set_global_prompt_files(*files: TeeStream):
471
471
  global GLOBAL_PROMPTS
472
- GLOBAL_PROMPTS = set(*files)
472
+ GLOBAL_PROMPTS = set(files)
473
473
 
474
474
 
475
475
  def _output_context(kwargs, key, encoding, errors, text):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iripau
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: Python utilities focused on command execution
5
5
  Author: Ricardo Quezada
6
6
  Maintainer: Ricardo Quezada
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "iripau"
7
- version = "1.1.0"
7
+ version = "1.1.2"
8
8
  dependencies = [
9
9
  "curlify",
10
10
  "psutil"
@@ -3,8 +3,22 @@ Tests to validate iripau.requests module
3
3
  """
4
4
 
5
5
  import pytest
6
+ import mock
6
7
 
7
8
  from iripau.requests import Session
9
+ from iripau.requests import delete
10
+ from iripau.requests import get
11
+ from iripau.requests import head
12
+ from iripau.requests import options
13
+ from iripau.requests import patch
14
+ from iripau.requests import post
15
+ from iripau.requests import put
16
+
17
+ URL = "https://some.url.com:8080/api"
18
+ KWARGS = {
19
+ "kwarg1": "Value-1",
20
+ "kwarg2": "Value-2"
21
+ }
8
22
 
9
23
 
10
24
  class TestRequests:
@@ -70,3 +84,45 @@ class TestRequests:
70
84
 
71
85
  assert response.request.headers["API-Key"] != "***"
72
86
  assert response.request.headers["Authorization"] != "***"
87
+
88
+ @mock.patch("iripau.requests.Session")
89
+ def test_delete(self, mock_session):
90
+ response = delete(URL, **KWARGS)
91
+ mock_session.return_value.delete.assert_called_once_with(URL, **KWARGS)
92
+ assert mock_session.return_value.delete.return_value == response
93
+
94
+ @mock.patch("iripau.requests.Session")
95
+ def test_get(self, mock_session):
96
+ response = get(URL, **KWARGS)
97
+ mock_session.return_value.get.assert_called_once_with(URL, **KWARGS)
98
+ assert mock_session.return_value.get.return_value == response
99
+
100
+ @mock.patch("iripau.requests.Session")
101
+ def test_head(self, mock_session):
102
+ response = head(URL, **KWARGS)
103
+ mock_session.return_value.head.assert_called_once_with(URL, **KWARGS)
104
+ assert mock_session.return_value.head.return_value == response
105
+
106
+ @mock.patch("iripau.requests.Session")
107
+ def test_options(self, mock_session):
108
+ response = options(URL, **KWARGS)
109
+ mock_session.return_value.options.assert_called_once_with(URL, **KWARGS)
110
+ assert mock_session.return_value.options.return_value == response
111
+
112
+ @mock.patch("iripau.requests.Session")
113
+ def test_patch(self, mock_session):
114
+ response = patch(URL, **KWARGS)
115
+ mock_session.return_value.patch.assert_called_once_with(URL, **KWARGS)
116
+ assert mock_session.return_value.patch.return_value == response
117
+
118
+ @mock.patch("iripau.requests.Session")
119
+ def test_post(self, mock_session):
120
+ response = post(URL, **KWARGS)
121
+ mock_session.return_value.post.assert_called_once_with(URL, **KWARGS)
122
+ assert mock_session.return_value.post.return_value == response
123
+
124
+ @mock.patch("iripau.requests.Session")
125
+ def test_put(self, mock_session):
126
+ response = put(URL, **KWARGS)
127
+ mock_session.return_value.put.assert_called_once_with(URL, **KWARGS)
128
+ assert mock_session.return_value.put.return_value == response
@@ -1,108 +0,0 @@
1
- """
2
- Execute commands as Python functions
3
- """
4
-
5
- from shlex import split
6
- from random import choice
7
- from itertools import chain
8
-
9
- from iripau.command import host_run
10
-
11
-
12
- class Command:
13
- """ Run an executable command as a Python callable """
14
-
15
- def __init__(self, parent, command):
16
- self._parent = parent
17
- self._command = parent._mk_command(command)
18
- self._mk_command = parent._mk_command
19
-
20
- def __getattr__(self, command):
21
- child = Command(self, command)
22
- setattr(self, command, child)
23
- return child
24
-
25
- def __call__(self, *args, **kwargs):
26
- return self._parent(self._command, *args, **kwargs)
27
-
28
-
29
- def make_command(command):
30
- return command.replace("_", "-")
31
-
32
-
33
- def make_option(option):
34
- return "--" + option.replace("_", "-"),
35
-
36
-
37
- class Executable:
38
- """ Run an executable as a Python callable """
39
-
40
- def __init__(
41
- self, executable, make_command=make_command, make_option=make_option,
42
- alias=None, run_args_prefix="_", run_function=None, **kwargs
43
- ):
44
- self._run = run_function or host_run
45
- self._exe = split(executable) if isinstance(executable, str) else executable
46
- self._alias = split(alias) if isinstance(alias, str) else alias
47
- self._kwargs = kwargs
48
-
49
- self._prefix = run_args_prefix
50
- self._mk_option = make_option
51
- self._mk_command = make_command
52
-
53
- def __getattr__(self, command):
54
- child = Command(self, command)
55
- setattr(self, command, child)
56
- return child
57
-
58
- def __call__(self, *args, **kwargs):
59
- optionals = chain.from_iterable(
60
- self._make_arg(self._mk_option(key), value)
61
- for key, value in kwargs.items()
62
- if not key.startswith(self._prefix)
63
- )
64
-
65
- positionals = list(map(str, args))
66
- optionals = list(optionals)
67
-
68
- kwargs = {
69
- key[len(self._prefix):]: value
70
- for key, value in kwargs.items()
71
- if key.startswith(self._prefix)
72
- }
73
-
74
- if self._alias:
75
- kwargs.setdefault("alias", self._alias + positionals + optionals)
76
-
77
- cmd = self._exe + positionals + optionals
78
- return self._run(cmd, **self._kwargs, **kwargs)
79
-
80
- @staticmethod
81
- def _is_iterable(value):
82
- if isinstance(value, (str, bytes)):
83
- return False
84
- return hasattr(value, "__iter__")
85
-
86
- @classmethod
87
- def _make_arg(cls, options, value=None):
88
- """ Return a list of tokens. Randomly choose a short or long option.
89
- If a 'value' is given it's appended appropriately.
90
- If 'value' is iterable, the option will be repeated for each item.
91
- """
92
- if cls._is_iterable(value):
93
- return chain.from_iterable(
94
- cls._make_arg(options, item)
95
- for item in value
96
- )
97
-
98
- if value in {None, False}:
99
- return []
100
-
101
- option = choice(options)
102
- if value is True:
103
- return [option]
104
-
105
- value = str(value)
106
- if option.startswith("--"):
107
- return [option + "=" + value]
108
- return [option, value]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes