cli2 3.3.31__tar.gz → 3.3.33__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.
- {cli2-3.3.31/cli2.egg-info → cli2-3.3.33}/PKG-INFO +1 -1
- {cli2-3.3.31 → cli2-3.3.33}/cli2/client.py +31 -28
- {cli2-3.3.31 → cli2-3.3.33}/cli2/group.py +24 -10
- {cli2-3.3.31 → cli2-3.3.33}/cli2/test_client.py +21 -5
- {cli2-3.3.31 → cli2-3.3.33}/cli2/test_group.py +2 -2
- {cli2-3.3.31 → cli2-3.3.33/cli2.egg-info}/PKG-INFO +1 -1
- {cli2-3.3.31 → cli2-3.3.33}/setup.py +1 -1
- {cli2-3.3.31 → cli2-3.3.33}/MANIFEST.in +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/README.rst +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/classifiers.txt +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/__init__.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/argument.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/asyncio.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/cli.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/colors.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/command.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/configuration.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/decorators.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/display.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/entry_point.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/example_client.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/example_client_complex.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/example_nesting.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/example_obj.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/logging.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/node.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/overrides.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/sphinx.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/table.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/test.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/test_cli.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/test_command.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/test_configuration.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/test_decorators.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/test_display.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/test_entry_point.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/test_inject.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/test_node.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2/test_table.py +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2.egg-info/SOURCES.txt +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2.egg-info/dependency_links.txt +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2.egg-info/entry_points.txt +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2.egg-info/requires.txt +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/cli2.egg-info/top_level.txt +0 -0
- {cli2-3.3.31 → cli2-3.3.33}/setup.cfg +0 -0
|
@@ -76,13 +76,17 @@ class Paginator:
|
|
|
76
76
|
|
|
77
77
|
:py:class:`Model` class or ``dict`` by default.
|
|
78
78
|
|
|
79
|
-
.. py:attribute::
|
|
79
|
+
.. py:attribute:: postfilter
|
|
80
80
|
|
|
81
|
-
Callback called for every item.
|
|
81
|
+
Callback called for every item after filtering.
|
|
82
|
+
|
|
83
|
+
.. py:attribute:: prefilter
|
|
84
|
+
|
|
85
|
+
Callback called for every item before filtering.
|
|
82
86
|
"""
|
|
83
87
|
|
|
84
88
|
def __init__(self, client, url, params=None, model=None, expressions=None,
|
|
85
|
-
|
|
89
|
+
prefilter=None, postfilter=None):
|
|
86
90
|
"""
|
|
87
91
|
Initialize a paginator object with a client on a URL with parameters.
|
|
88
92
|
|
|
@@ -98,7 +102,8 @@ class Paginator:
|
|
|
98
102
|
self.page_start = 1
|
|
99
103
|
self.per_page = None
|
|
100
104
|
self.initialized = False
|
|
101
|
-
self.
|
|
105
|
+
self.prefilter = prefilter
|
|
106
|
+
self.postfilter = postfilter
|
|
102
107
|
self.expressions = []
|
|
103
108
|
for expression in (expressions or []):
|
|
104
109
|
if not isinstance(expression, Expression):
|
|
@@ -272,11 +277,12 @@ class Paginator:
|
|
|
272
277
|
await self.initialize(response)
|
|
273
278
|
return response
|
|
274
279
|
|
|
275
|
-
async def __aiter__(self,
|
|
280
|
+
async def __aiter__(self, prefilter=None, postfilter=None):
|
|
276
281
|
"""
|
|
277
282
|
Asynchronous iterator.
|
|
278
283
|
"""
|
|
279
|
-
|
|
284
|
+
prefilter = prefilter or self.prefilter
|
|
285
|
+
postfilter = postfilter or self.postfilter
|
|
280
286
|
|
|
281
287
|
if self._reverse and not self.total_pages:
|
|
282
288
|
first_page_response = await self.page_response(1)
|
|
@@ -286,18 +292,24 @@ class Paginator:
|
|
|
286
292
|
|
|
287
293
|
python_filter = self.python_filter()
|
|
288
294
|
|
|
295
|
+
async def yielder(items):
|
|
296
|
+
for item in items:
|
|
297
|
+
if prefilter:
|
|
298
|
+
await async_resolve(prefilter(item))
|
|
299
|
+
if not python_filter or python_filter.matches(item):
|
|
300
|
+
if postfilter:
|
|
301
|
+
await async_resolve(postfilter(item))
|
|
302
|
+
yield item
|
|
303
|
+
|
|
289
304
|
while items := await self.page_items(page):
|
|
290
|
-
if items
|
|
305
|
+
if not items:
|
|
291
306
|
continue
|
|
292
307
|
|
|
293
308
|
if self._reverse:
|
|
294
309
|
items = reversed(items)
|
|
295
310
|
|
|
296
|
-
for item in items:
|
|
297
|
-
|
|
298
|
-
if callback:
|
|
299
|
-
callback(item)
|
|
300
|
-
yield item
|
|
311
|
+
async for item in yielder(items):
|
|
312
|
+
yield item
|
|
301
313
|
|
|
302
314
|
if self._reverse:
|
|
303
315
|
page -= 1
|
|
@@ -306,11 +318,8 @@ class Paginator:
|
|
|
306
318
|
if page == 1:
|
|
307
319
|
# use cached first page response
|
|
308
320
|
items = self.response_items(first_page_response)
|
|
309
|
-
for item in reversed(items):
|
|
310
|
-
|
|
311
|
-
if callback:
|
|
312
|
-
callback(item)
|
|
313
|
-
yield item
|
|
321
|
+
async for item in yielder(reversed(items)):
|
|
322
|
+
yield item
|
|
314
323
|
break
|
|
315
324
|
else:
|
|
316
325
|
if page == self.total_pages:
|
|
@@ -691,13 +700,7 @@ class ModelGroup(Group):
|
|
|
691
700
|
dict(model=cls),
|
|
692
701
|
)
|
|
693
702
|
)
|
|
694
|
-
|
|
695
|
-
url_list_methods = ['find', 'get', 'delete', 'create']
|
|
696
|
-
if cls.url_list:
|
|
697
|
-
for name in url_list_methods:
|
|
698
|
-
self.cmd(getattr(cls, name))
|
|
699
|
-
|
|
700
|
-
self.load_cls(cls, exclude=url_list_methods)
|
|
703
|
+
self.load_cls(cls)
|
|
701
704
|
|
|
702
705
|
|
|
703
706
|
class ModelMetaclass(type):
|
|
@@ -811,7 +814,7 @@ class Model(metaclass=ModelMetaclass):
|
|
|
811
814
|
|
|
812
815
|
@classmethod
|
|
813
816
|
@hide('expressions')
|
|
814
|
-
@cmd(color='green')
|
|
817
|
+
@cmd(color='green', condition=lambda cls: cls.url_list)
|
|
815
818
|
def find(cls, *expressions, **params):
|
|
816
819
|
"""
|
|
817
820
|
Find objects filtered by GET params
|
|
@@ -865,7 +868,7 @@ class Model(metaclass=ModelMetaclass):
|
|
|
865
868
|
raise Exception(f'{type(self).__name__}.url_list not set')
|
|
866
869
|
return self.url_detail.format(self=self)
|
|
867
870
|
|
|
868
|
-
@cmd(color='red')
|
|
871
|
+
@cmd(color='red', condition=lambda cls: cls.url_list)
|
|
869
872
|
async def delete(self):
|
|
870
873
|
"""
|
|
871
874
|
Delete model.
|
|
@@ -875,7 +878,7 @@ class Model(metaclass=ModelMetaclass):
|
|
|
875
878
|
return await self.client.delete(self.url)
|
|
876
879
|
|
|
877
880
|
@classmethod
|
|
878
|
-
@cmd(doc="""
|
|
881
|
+
@cmd(condition=lambda cls: cls.url_list, doc="""
|
|
879
882
|
POST request to create.
|
|
880
883
|
|
|
881
884
|
Example:
|
|
@@ -891,7 +894,7 @@ class Model(metaclass=ModelMetaclass):
|
|
|
891
894
|
return obj
|
|
892
895
|
|
|
893
896
|
@classmethod
|
|
894
|
-
@cmd(color='green', doc="""
|
|
897
|
+
@cmd(color='green', condition=lambda cls: cls.url_list, doc="""
|
|
895
898
|
Get a model based on kwargs.
|
|
896
899
|
|
|
897
900
|
Example:
|
|
@@ -177,21 +177,35 @@ class Group(EntryPoint, dict):
|
|
|
177
177
|
self.group(name).load(target, parent=obj)
|
|
178
178
|
return self
|
|
179
179
|
|
|
180
|
-
def load_cls(self, cls,
|
|
180
|
+
def load_cls(self, cls, leaf=None):
|
|
181
181
|
"""
|
|
182
182
|
Load all methods which have been decorated with @cmd
|
|
183
|
+
|
|
184
|
+
Note that you can define conditions, this is how we hide functions such
|
|
185
|
+
as create/delete/get from models without url_list:
|
|
186
|
+
|
|
187
|
+
.. code-block:: python
|
|
188
|
+
|
|
189
|
+
@cli2.cmd(condition=lambda cls: cls.url_list)
|
|
183
190
|
"""
|
|
184
|
-
|
|
191
|
+
leaf = leaf if leaf else cls
|
|
192
|
+
for base in cls.__bases__:
|
|
193
|
+
self.load_cls(base, leaf=leaf)
|
|
194
|
+
|
|
185
195
|
for name, method in cls.__dict__.items():
|
|
186
|
-
if name in exclude:
|
|
187
|
-
continue
|
|
188
196
|
wrapped_method = getattr(method, '__func__', None)
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
197
|
+
cfg = getattr(
|
|
198
|
+
wrapped_method,
|
|
199
|
+
'cli2',
|
|
200
|
+
getattr(method, 'cli2', None),
|
|
201
|
+
)
|
|
202
|
+
if cfg is None:
|
|
203
|
+
continue
|
|
204
|
+
condition = cfg.get('condition', None)
|
|
205
|
+
if condition:
|
|
206
|
+
if not condition(leaf):
|
|
207
|
+
continue
|
|
208
|
+
self.cmd(method)
|
|
195
209
|
|
|
196
210
|
def __call__(self, *argv):
|
|
197
211
|
self.exit_code = 0
|
|
@@ -68,6 +68,15 @@ async def test_client_cli_override(client_class, httpx_mock):
|
|
|
68
68
|
return cls.url_list
|
|
69
69
|
assert await Client.cli['testmodel']['find'].async_call('bar') == 'bar/foo'
|
|
70
70
|
|
|
71
|
+
class TestModel2(Client.Model):
|
|
72
|
+
@classmethod
|
|
73
|
+
@cli2.cmd
|
|
74
|
+
async def find(cls):
|
|
75
|
+
return 'foo'
|
|
76
|
+
|
|
77
|
+
assert await Client.cli['testmodel2']['find'].async_call() == 'foo'
|
|
78
|
+
assert 'get' not in Client.cli['testmodel2']
|
|
79
|
+
|
|
71
80
|
|
|
72
81
|
def test_client_model(client_class):
|
|
73
82
|
assert issubclass(client_class.Model, cli2.Model)
|
|
@@ -348,11 +357,18 @@ async def test_pagination(httpx_mock):
|
|
|
348
357
|
httpx_mock.add_response(url='http://lol/?page=3', json=[])
|
|
349
358
|
client = Client(base_url='http://lol')
|
|
350
359
|
paginator = client.paginate('/')
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
360
|
+
|
|
361
|
+
def prefilter(item):
|
|
362
|
+
item['b'] = item['a']
|
|
363
|
+
|
|
364
|
+
def postfilter(item):
|
|
365
|
+
item['c'] = item['b']
|
|
366
|
+
|
|
367
|
+
paginator.prefilter = prefilter
|
|
368
|
+
paginator.postfilter = postfilter
|
|
369
|
+
assert await paginator.list() == [
|
|
370
|
+
dict(a=1, b=1, c=1),
|
|
371
|
+
dict(a=2, b=2, c=2)
|
|
356
372
|
]
|
|
357
373
|
|
|
358
374
|
|
|
@@ -225,11 +225,11 @@ def test_load_cls():
|
|
|
225
225
|
def test2(self):
|
|
226
226
|
pass
|
|
227
227
|
|
|
228
|
-
@cli2.cmd
|
|
228
|
+
@cli2.cmd(condition=lambda cls: False)
|
|
229
229
|
def exclude(self):
|
|
230
230
|
pass
|
|
231
231
|
group = Group()
|
|
232
|
-
group.load_cls(Foo
|
|
232
|
+
group.load_cls(Foo)
|
|
233
233
|
assert 'bar' not in group
|
|
234
234
|
assert 'test' in group
|
|
235
235
|
assert 'test2' in group
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|