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.
Files changed (45) hide show
  1. {cli2-3.3.31/cli2.egg-info → cli2-3.3.33}/PKG-INFO +1 -1
  2. {cli2-3.3.31 → cli2-3.3.33}/cli2/client.py +31 -28
  3. {cli2-3.3.31 → cli2-3.3.33}/cli2/group.py +24 -10
  4. {cli2-3.3.31 → cli2-3.3.33}/cli2/test_client.py +21 -5
  5. {cli2-3.3.31 → cli2-3.3.33}/cli2/test_group.py +2 -2
  6. {cli2-3.3.31 → cli2-3.3.33/cli2.egg-info}/PKG-INFO +1 -1
  7. {cli2-3.3.31 → cli2-3.3.33}/setup.py +1 -1
  8. {cli2-3.3.31 → cli2-3.3.33}/MANIFEST.in +0 -0
  9. {cli2-3.3.31 → cli2-3.3.33}/README.rst +0 -0
  10. {cli2-3.3.31 → cli2-3.3.33}/classifiers.txt +0 -0
  11. {cli2-3.3.31 → cli2-3.3.33}/cli2/__init__.py +0 -0
  12. {cli2-3.3.31 → cli2-3.3.33}/cli2/argument.py +0 -0
  13. {cli2-3.3.31 → cli2-3.3.33}/cli2/asyncio.py +0 -0
  14. {cli2-3.3.31 → cli2-3.3.33}/cli2/cli.py +0 -0
  15. {cli2-3.3.31 → cli2-3.3.33}/cli2/colors.py +0 -0
  16. {cli2-3.3.31 → cli2-3.3.33}/cli2/command.py +0 -0
  17. {cli2-3.3.31 → cli2-3.3.33}/cli2/configuration.py +0 -0
  18. {cli2-3.3.31 → cli2-3.3.33}/cli2/decorators.py +0 -0
  19. {cli2-3.3.31 → cli2-3.3.33}/cli2/display.py +0 -0
  20. {cli2-3.3.31 → cli2-3.3.33}/cli2/entry_point.py +0 -0
  21. {cli2-3.3.31 → cli2-3.3.33}/cli2/example_client.py +0 -0
  22. {cli2-3.3.31 → cli2-3.3.33}/cli2/example_client_complex.py +0 -0
  23. {cli2-3.3.31 → cli2-3.3.33}/cli2/example_nesting.py +0 -0
  24. {cli2-3.3.31 → cli2-3.3.33}/cli2/example_obj.py +0 -0
  25. {cli2-3.3.31 → cli2-3.3.33}/cli2/logging.py +0 -0
  26. {cli2-3.3.31 → cli2-3.3.33}/cli2/node.py +0 -0
  27. {cli2-3.3.31 → cli2-3.3.33}/cli2/overrides.py +0 -0
  28. {cli2-3.3.31 → cli2-3.3.33}/cli2/sphinx.py +0 -0
  29. {cli2-3.3.31 → cli2-3.3.33}/cli2/table.py +0 -0
  30. {cli2-3.3.31 → cli2-3.3.33}/cli2/test.py +0 -0
  31. {cli2-3.3.31 → cli2-3.3.33}/cli2/test_cli.py +0 -0
  32. {cli2-3.3.31 → cli2-3.3.33}/cli2/test_command.py +0 -0
  33. {cli2-3.3.31 → cli2-3.3.33}/cli2/test_configuration.py +0 -0
  34. {cli2-3.3.31 → cli2-3.3.33}/cli2/test_decorators.py +0 -0
  35. {cli2-3.3.31 → cli2-3.3.33}/cli2/test_display.py +0 -0
  36. {cli2-3.3.31 → cli2-3.3.33}/cli2/test_entry_point.py +0 -0
  37. {cli2-3.3.31 → cli2-3.3.33}/cli2/test_inject.py +0 -0
  38. {cli2-3.3.31 → cli2-3.3.33}/cli2/test_node.py +0 -0
  39. {cli2-3.3.31 → cli2-3.3.33}/cli2/test_table.py +0 -0
  40. {cli2-3.3.31 → cli2-3.3.33}/cli2.egg-info/SOURCES.txt +0 -0
  41. {cli2-3.3.31 → cli2-3.3.33}/cli2.egg-info/dependency_links.txt +0 -0
  42. {cli2-3.3.31 → cli2-3.3.33}/cli2.egg-info/entry_points.txt +0 -0
  43. {cli2-3.3.31 → cli2-3.3.33}/cli2.egg-info/requires.txt +0 -0
  44. {cli2-3.3.31 → cli2-3.3.33}/cli2.egg-info/top_level.txt +0 -0
  45. {cli2-3.3.31 → cli2-3.3.33}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cli2
3
- Version: 3.3.31
3
+ Version: 3.3.33
4
4
  Summary: image:: https://yourlabs.io/oss/cli2/badges/master/pipeline.svg
5
5
  Home-page: https://yourlabs.io/oss/cli2
6
6
  Author: James Pic
@@ -76,13 +76,17 @@ class Paginator:
76
76
 
77
77
  :py:class:`Model` class or ``dict`` by default.
78
78
 
79
- .. py:attribute:: callback
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
- callback=None):
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.callback = callback
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, callback=None):
280
+ async def __aiter__(self, prefilter=None, postfilter=None):
276
281
  """
277
282
  Asynchronous iterator.
278
283
  """
279
- callback = callback or self.callback
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 == 'continue':
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
- if not python_filter or python_filter.matches(item):
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
- if not python_filter or python_filter.matches(item):
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, exclude=None):
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
- exclude = exclude or []
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
- if hasattr(wrapped_method, 'cli2'):
190
- self.cmd(wrapped_method)
191
- elif hasattr(method, 'cli2'):
192
- self.cmd(method)
193
- for base in cls.__bases__:
194
- self.load_cls(base, exclude=exclude)
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
- paginator.callback = mock.Mock()
352
- assert await paginator.list() == [dict(a=1), dict(a=2)]
353
- assert paginator.callback.call_args_list == [
354
- mock.call({'a': 1}),
355
- mock.call({'a': 2})
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, exclude=['exclude'])
232
+ group.load_cls(Foo)
233
233
  assert 'bar' not in group
234
234
  assert 'test' in group
235
235
  assert 'test2' in group
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cli2
3
- Version: 3.3.31
3
+ Version: 3.3.33
4
4
  Summary: image:: https://yourlabs.io/oss/cli2/badges/master/pipeline.svg
5
5
  Home-page: https://yourlabs.io/oss/cli2
6
6
  Author: James Pic
@@ -3,7 +3,7 @@ from setuptools import setup
3
3
 
4
4
  setup(
5
5
  name='cli2',
6
- version='3.3.31',
6
+ version='3.3.33',
7
7
  setup_requires='setupmeta',
8
8
  install_requires=[
9
9
  'docstring_parser',
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