fastcore 2.2.23__tar.gz → 2.2.24__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 (43) hide show
  1. {fastcore-2.2.23/fastcore.egg-info → fastcore-2.2.24}/PKG-INFO +2 -1
  2. {fastcore-2.2.23 → fastcore-2.2.24}/README.md +1 -0
  3. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/__init__.py +2 -2
  4. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/_modidx.py +18 -1
  5. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/script.py +4 -1
  6. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/xml.py +140 -5
  7. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/xtras.py +6 -11
  8. {fastcore-2.2.23 → fastcore-2.2.24/fastcore.egg-info}/PKG-INFO +2 -1
  9. {fastcore-2.2.23 → fastcore-2.2.24}/CONTRIBUTING.md +0 -0
  10. {fastcore-2.2.23 → fastcore-2.2.24}/LICENSE +0 -0
  11. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/aio.py +0 -0
  12. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/all.py +0 -0
  13. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/ansi.py +0 -0
  14. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/apisurface.py +0 -0
  15. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/basics.py +0 -0
  16. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/dispatch.py +0 -0
  17. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/docments.py +0 -0
  18. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/docscrape.py +0 -0
  19. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/editskill.py +0 -0
  20. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/foundation.py +0 -0
  21. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/funccall.py +0 -0
  22. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/imghdr.py +0 -0
  23. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/imports.py +0 -0
  24. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/meta.py +0 -0
  25. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/nb_imports.py +0 -0
  26. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/nbio.py +0 -0
  27. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/net.py +0 -0
  28. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/parallel.py +0 -0
  29. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/py2pyi.py +0 -0
  30. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/shutil.py +0 -0
  31. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/style.py +0 -0
  32. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/test.py +0 -0
  33. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/tools.py +0 -0
  34. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/transform.py +0 -0
  35. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/utils.py +0 -0
  36. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore/xdg.py +0 -0
  37. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore.egg-info/SOURCES.txt +0 -0
  38. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore.egg-info/dependency_links.txt +0 -0
  39. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore.egg-info/entry_points.txt +0 -0
  40. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore.egg-info/requires.txt +0 -0
  41. {fastcore-2.2.23 → fastcore-2.2.24}/fastcore.egg-info/top_level.txt +0 -0
  42. {fastcore-2.2.23 → fastcore-2.2.24}/pyproject.toml +0 -0
  43. {fastcore-2.2.23 → fastcore-2.2.24}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastcore
3
- Version: 2.2.23
3
+ Version: 2.2.24
4
4
  Summary: Python supercharged for fastai development
5
5
  Author-email: Jeremy Howard and Sylvain Gugger <infos@fast.ai>
6
6
  License: Apache-2.0
@@ -84,6 +84,7 @@ To install fastcore run: `conda install fastcore -c fastai` (if you use Anaconda
84
84
  - `fastcore.test`: Simple testing functions
85
85
  - `fastcore.foundation`: Mixins, delegation, composition, and more
86
86
  - `fastcore.xtras`: Utility functions to help with functional-style programming, parallel processing, and more
87
+ - `fastcore.xml`: HTML with [`ft`](https://fastcore.fast.ai/xml.html#ft), and case-preserving, namespace-aware XML with [`E`](https://fastcore.fast.ai/xml.html#e) factories
87
88
 
88
89
  To get started, we recommend you read through [the fastcore tour](https://fastcore.fast.ai/tour.html).
89
90
 
@@ -53,6 +53,7 @@ To install fastcore run: `conda install fastcore -c fastai` (if you use Anaconda
53
53
  - `fastcore.test`: Simple testing functions
54
54
  - `fastcore.foundation`: Mixins, delegation, composition, and more
55
55
  - `fastcore.xtras`: Utility functions to help with functional-style programming, parallel processing, and more
56
+ - `fastcore.xml`: HTML with [`ft`](https://fastcore.fast.ai/xml.html#ft), and case-preserving, namespace-aware XML with [`E`](https://fastcore.fast.ai/xml.html#e) factories
56
57
 
57
58
  To get started, we recommend you read through [the fastcore tour](https://fastcore.fast.ai/tour.html).
58
59
 
@@ -52,7 +52,7 @@ Modules:
52
52
  - `fastcore.test`: Helper functions to quickly write tests in notebooks
53
53
  - `fastcore.tools`: Text and file editing primitives shared by the fastai editing tools
54
54
  - `fastcore.xdg`: XDG Base Directory Specification helpers.
55
- - `fastcore.xml`: Concise generation of XML.
55
+ - `fastcore.xml`: Concise HTML generation and namespace-aware XML construction.
56
56
  - `fastcore.xtras`: Utility functions used in the fastai library"""
57
57
 
58
- __version__ = "2.2.23"
58
+ __version__ = "2.2.24"
@@ -812,7 +812,12 @@ d = { 'settings': { 'branch': 'main',
812
812
  'fastcore.xdg.xdg_data_home': ('xdg.html#xdg_data_home', 'fastcore/xdg.py'),
813
813
  'fastcore.xdg.xdg_runtime_dir': ('xdg.html#xdg_runtime_dir', 'fastcore/xdg.py'),
814
814
  'fastcore.xdg.xdg_state_home': ('xdg.html#xdg_state_home', 'fastcore/xdg.py')},
815
- 'fastcore.xml': { 'fastcore.xml.FT': ('xml.html#ft', 'fastcore/xml.py'),
815
+ 'fastcore.xml': { 'fastcore.xml.E': ('xml.html#e', 'fastcore/xml.py'),
816
+ 'fastcore.xml.E.__call__': ('xml.html#e.__call__', 'fastcore/xml.py'),
817
+ 'fastcore.xml.E.__getattr__': ('xml.html#e.__getattr__', 'fastcore/xml.py'),
818
+ 'fastcore.xml.E.__init__': ('xml.html#e.__init__', 'fastcore/xml.py'),
819
+ 'fastcore.xml.E._namespace': ('xml.html#e._namespace', 'fastcore/xml.py'),
820
+ 'fastcore.xml.FT': ('xml.html#ft', 'fastcore/xml.py'),
816
821
  'fastcore.xml.FT.__call__': ('xml.html#ft.__call__', 'fastcore/xml.py'),
817
822
  'fastcore.xml.FT.__eq__': ('xml.html#ft.__eq__', 'fastcore/xml.py'),
818
823
  'fastcore.xml.FT.__getattr__': ('xml.html#ft.__getattr__', 'fastcore/xml.py'),
@@ -833,6 +838,13 @@ d = { 'settings': { 'branch': 'main',
833
838
  'fastcore.xml.Html': ('xml.html#html', 'fastcore/xml.py'),
834
839
  'fastcore.xml.Safe': ('xml.html#safe', 'fastcore/xml.py'),
835
840
  'fastcore.xml.Safe.__html__': ('xml.html#safe.__html__', 'fastcore/xml.py'),
841
+ 'fastcore.xml.XML': ('xml.html#xml', 'fastcore/xml.py'),
842
+ 'fastcore.xml.XML.__init__': ('xml.html#xml.__init__', 'fastcore/xml.py'),
843
+ 'fastcore.xml.XML.__str__': ('xml.html#xml.__str__', 'fastcore/xml.py'),
844
+ 'fastcore.xml.XML._child': ('xml.html#xml._child', 'fastcore/xml.py'),
845
+ 'fastcore.xml.XML._render': ('xml.html#xml._render', 'fastcore/xml.py'),
846
+ 'fastcore.xml.XML._repr_markdown_': ('xml.html#xml._repr_markdown_', 'fastcore/xml.py'),
847
+ 'fastcore.xml.XML.bytes': ('xml.html#xml.bytes', 'fastcore/xml.py'),
836
848
  'fastcore.xml._attr2tag': ('xml.html#_attr2tag', 'fastcore/xml.py'),
837
849
  'fastcore.xml._escape': ('xml.html#_escape', 'fastcore/xml.py'),
838
850
  'fastcore.xml._fix_k': ('xml.html#_fix_k', 'fastcore/xml.py'),
@@ -841,6 +853,11 @@ d = { 'settings': { 'branch': 'main',
841
853
  'fastcore.xml._preproc': ('xml.html#_preproc', 'fastcore/xml.py'),
842
854
  'fastcore.xml._to_attr': ('xml.html#_to_attr', 'fastcore/xml.py'),
843
855
  'fastcore.xml._to_xml': ('xml.html#_to_xml', 'fastcore/xml.py'),
856
+ 'fastcore.xml._xml_flatten': ('xml.html#_xml_flatten', 'fastcore/xml.py'),
857
+ 'fastcore.xml._xml_keyword': ('xml.html#_xml_keyword', 'fastcore/xml.py'),
858
+ 'fastcore.xml._xml_name': ('xml.html#_xml_name', 'fastcore/xml.py'),
859
+ 'fastcore.xml._xml_qualify': ('xml.html#_xml_qualify', 'fastcore/xml.py'),
860
+ 'fastcore.xml._xml_value': ('xml.html#_xml_value', 'fastcore/xml.py'),
844
861
  'fastcore.xml.attrmap': ('xml.html#attrmap', 'fastcore/xml.py'),
845
862
  'fastcore.xml.dict2xml': ('xml.html#dict2xml', 'fastcore/xml.py'),
846
863
  'fastcore.xml.ft': ('xml.html#ft', 'fastcore/xml.py'),
@@ -69,6 +69,8 @@ Union types such as `int|str` try each type in turn, and `enum` types such as th
69
69
 
70
70
  The decorated function is always the CLI entry point, and only the entry point gets `sys.argv`: argv is parsed when the function's file is run directly (`python foo.py`, `python -m foo`, or `%run foo.py`), or when it's called with no arguments from the top-level body of a directly-run file (which is how console script wrappers invoke it). Every other call is a plain Python call that ignores argv, whether from a notebook, the REPL, another function, or another `call_parse` function.
71
71
 
72
+ CLI calls return only `int` values (including `bool`) for console script exit codes; other return values are discarded. Plain Python calls preserve the return value. To report an error message, raise `CliError` rather than returning a string.
73
+
72
74
  Use the `nested` keyword argument to create nested parsers, where earlier parsers consume only their known args from `sys.argv` before later parsers are used. This is useful to create one command line application that executes another. For example:
73
75
 
74
76
  ```sh
@@ -301,7 +303,8 @@ def _run_cli(func, nested, pos=None):
301
303
  pargs,args = _pos_split(func, merge(args, args_from_prog(func, xtra)))
302
304
  try:
303
305
  res = tfunc(*pargs, **args)
304
- return asyncio.run(res) if inspect.isawaitable(res) else res
306
+ if inspect.isawaitable(res): res = asyncio.run(res)
307
+ return res if isinstance(res, int) else None
305
308
  except CliError as e: raise SystemExit(str(e))
306
309
 
307
310
  def call_parse(func=None, nested=False, pos:list=None):
@@ -1,4 +1,4 @@
1
- """Concise generation of XML.
1
+ """Concise HTML generation and namespace-aware XML construction.
2
2
 
3
3
  `ft(tag, *children, **attrs)` builds an `FT` tree — a lightweight XML/HTML element — and `to_xml` renders it (this is the foundation [fasthtml](https://fastht.ml) builds on). The common HTML tags are predefined as `ft` partials (`Div`, `P`, `A`, ...). Attributes are passed as keywords: use `klass` and `fr` for the reserved words `class` and `for`, `_` in an attr name becomes `-`, and attrs can be got and set on the object directly. Children can also be supplied after the attrs by calling the element, e.g. `Div(id='x')(child1, child2)`:
4
4
 
@@ -8,16 +8,18 @@ test_eq(to_xml(t), '<div hx-get="/get">\n <p class="a">hi</p>\n</div>\n')
8
8
  test_eq(to_xml(Div(id='x')('child')), '<div id="x">child</div>\n')
9
9
  ```
10
10
 
11
+ `E(prefix='', attr_ns=None, ns=None)` creates a namespace-bound XML factory. `e = E('w', attr_ns='w', ns={'w': uri})` supports expressions such as `e.tcW(type='dxa', w=2400)`. Tags and attributes preserve case. `prefix__name` selects an attribute namespace; `attrs_` accepts literal names. Expressions compose with text and ordered collections, retain their namespace bindings, and serialize as UTF-8 with `.bytes()`. XML text is escaped and booleans become `true`/`false`. The existing `ft` and `to_xml` APIs keep their HTML behavior.
12
+
11
13
  Docs: https://fastcore.fast.ai/xml.html.md"""
12
14
 
13
15
  # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/09_xml.ipynb.
14
16
 
15
17
  # %% auto #0
16
18
  __all__ = ['voids', 'attrmap', 'valmap', 'FT', 'ft', 'Html', 'Safe', 'to_xml', 'dict2xml', 'highlight', 'showtags', 'mk_getattr',
17
- 'Head', 'Title', 'Meta', 'Link', 'Style', 'Body', 'Pre', 'Code', 'Div', 'Span', 'P', 'H1', 'H2', 'H3', 'H4',
18
- 'H5', 'H6', 'Strong', 'Em', 'B', 'I', 'U', 'S', 'Strike', 'Sub', 'Sup', 'Hr', 'Br', 'Img', 'A', 'Nav', 'Ul',
19
- 'Ol', 'Li', 'Dl', 'Dt', 'Dd', 'Table', 'Thead', 'Tbody', 'Tfoot', 'Tr', 'Th', 'Td', 'Caption', 'Col',
20
- 'Colgroup', 'Form', 'Input', 'Textarea', 'Button', 'Select', 'Option', 'Label', 'Fieldset', 'Legend',
19
+ 'XML', 'E', 'Head', 'Title', 'Meta', 'Link', 'Style', 'Body', 'Pre', 'Code', 'Div', 'Span', 'P', 'H1', 'H2',
20
+ 'H3', 'H4', 'H5', 'H6', 'Strong', 'Em', 'B', 'I', 'U', 'S', 'Strike', 'Sub', 'Sup', 'Hr', 'Br', 'Img', 'A',
21
+ 'Nav', 'Ul', 'Ol', 'Li', 'Dl', 'Dt', 'Dd', 'Table', 'Thead', 'Tbody', 'Tfoot', 'Tr', 'Th', 'Td', 'Caption',
22
+ 'Col', 'Colgroup', 'Form', 'Input', 'Textarea', 'Button', 'Select', 'Option', 'Label', 'Fieldset', 'Legend',
21
23
  'Details', 'Summary', 'Main', 'Header', 'Footer', 'Section', 'Article', 'Aside', 'Figure', 'Figcaption',
22
24
  'Mark', 'Small', 'Iframe', 'Object', 'Embed', 'Param', 'Video', 'Audio', 'Source', 'Canvas', 'Svg', 'Math',
23
25
  'Script', 'Noscript', 'Template', 'Slot']
@@ -272,3 +274,136 @@ def mk_getattr(f):
272
274
  return _getattr
273
275
 
274
276
  __getattr__ = mk_getattr(ft)
277
+
278
+ # %% ../nbs/09_xml.ipynb #d6b837e2
279
+ from collections.abc import Iterator
280
+ from keyword import iskeyword
281
+ from xml.sax.saxutils import escape as xml_escape, quoteattr
282
+
283
+
284
+ # %% ../nbs/09_xml.ipynb #8cb06993
285
+ _xml_uri = 'http://www.w3.org/XML/1998/namespace'
286
+ _xmlns_uri = 'http://www.w3.org/2000/xmlns/'
287
+ _xml_start = (r'A-Z_a-z\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02ff\u0370-\u037d\u037f-\u1fff'
288
+ r'\u200c-\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd\U00010000-\U000effff')
289
+ _xml_local = re.compile(fr'[{_xml_start}][{_xml_start}\-.0-9\u00b7\u0300-\u036f\u203f-\u2040]*')
290
+ _xml_illegal = re.compile(r'[^\x09\x0a\x0d\x20-\ud7ff\ue000-\ufffd\U00010000-\U0010ffff]')
291
+
292
+ # %% ../nbs/09_xml.ipynb #b1031eff
293
+ def _xml_name(name):
294
+ parts = name.split(':')
295
+ if len(parts)>2 or any(not _xml_local.fullmatch(p) for p in parts): raise ValueError(f'Invalid XML name: {name!r}')
296
+ if parts[0]=='xmlns': raise ValueError('Use ns for namespace declarations')
297
+ return (parts[0], parts[1]) if len(parts)==2 else ('', name)
298
+
299
+ def _xml_value(value):
300
+ value = ('true' if value else 'false') if isinstance(value, bool) else str(value)
301
+ if _xml_illegal.search(value): raise ValueError('Illegal XML character')
302
+ return value
303
+
304
+ def _xml_flatten(children):
305
+ for child in children:
306
+ if isinstance(child, (list, tuple, L, Iterator)): yield from _xml_flatten(child)
307
+ elif child is not None: yield child
308
+
309
+ def _xml_qualify(name, prefix): return name if ':' in name or not prefix else f'{prefix}:{name}'
310
+
311
+ def _xml_keyword(name):
312
+ prefix, sep, local = name.partition('__')
313
+ if not sep: local = prefix
314
+ if local.endswith('_') and iskeyword(local[:-1]): local = local[:-1]
315
+ return f'{prefix}:{local}' if sep else local
316
+
317
+ # %% ../nbs/09_xml.ipynb #4abe3b40
318
+ class XML:
319
+ "A detached XML expression with fixed namespace bindings."
320
+ def __init__(self, tag, children, attrs, ns):
321
+ self.tag,self.attrs,self.ns = tag,attrs,ns
322
+ self.children = tuple(self._child(c) for c in _xml_flatten(children))
323
+
324
+ def _child(self, child):
325
+ if isinstance(child, XML): return child
326
+ if isinstance(child, str): return _xml_value(child)
327
+ raise TypeError('XML children require expressions, strings, or ordered collections of them')
328
+
329
+
330
+ # %% ../nbs/09_xml.ipynb #642fc6f0
331
+ @patch
332
+ def _render(self:XML, inherited, output):
333
+ output.append(f'<{self.tag}')
334
+ for prefix, uri in self.ns.items():
335
+ if inherited.get(prefix) != uri:
336
+ name = f'xmlns:{prefix}' if prefix else 'xmlns'
337
+ output.append(f' {name}={quoteattr(uri)}')
338
+ for name, value in self.attrs: output.append(f' {name}={quoteattr(value)}')
339
+ if not self.children:
340
+ output.append('/>')
341
+ return
342
+ output.append('>')
343
+ context = {**inherited, **self.ns}
344
+ for child in self.children:
345
+ if isinstance(child, XML): child._render(context, output)
346
+ else: output.append(xml_escape(child, {'\r': '&#13;'}))
347
+ output.append(f'</{self.tag}>')
348
+
349
+ @patch
350
+ def __str__(self:XML):
351
+ output = []
352
+ self._render({'xml': _xml_uri}, output)
353
+ return ''.join(output)
354
+
355
+ @patch
356
+ def bytes(self:XML):
357
+ "Serialize as UTF-8, without formatting whitespace."
358
+ return str(self).encode('utf-8')
359
+
360
+ @patch
361
+ def _repr_markdown_(self:XML): return f'```xml\n{self}\n```'
362
+
363
+ # %% ../nbs/09_xml.ipynb #335a7366
364
+ class E:
365
+ "Create a namespace-bound XML factory."
366
+ _node_cls = XML
367
+
368
+ def __init__(
369
+ self, prefix='', # Default element prefix; empty uses the default namespace
370
+ *, attr_ns=None, # Default attribute prefix; None leaves attributes unqualified
371
+ ns=None, # Prefix-to-URI bindings, including prefixes used only in values
372
+ ):
373
+ self._prefix,self._attr_ns = prefix,attr_ns
374
+ self._ns = {'': '', 'xml': _xml_uri, **(ns or {})}
375
+ for p in (prefix, attr_ns):
376
+ if p and (not _xml_local.fullmatch(p) or p=='xmlns'): raise ValueError(f'Invalid XML prefix: {p!r}')
377
+ for p, uri in self._ns.items():
378
+ if p and not _xml_local.fullmatch(p): raise ValueError(f'Invalid XML prefix: {p!r}')
379
+ if not isinstance(p, str) or not isinstance(uri, str): raise TypeError('Namespace prefixes and URIs require strings')
380
+ _xml_value(uri)
381
+ if p=='xmlns' or uri==_xmlns_uri or (p=='xml')!=(uri==_xml_uri) or p and not uri:
382
+ raise ValueError('Invalid reserved or empty namespace binding')
383
+
384
+ def _namespace(self, prefix):
385
+ if prefix not in self._ns: raise ValueError(f'Unknown XML prefix: {prefix!r}; supply ns')
386
+ return self._ns[prefix]
387
+
388
+ def __getattr__(self, name):
389
+ if name.startswith('_'): raise AttributeError(name)
390
+ if name.endswith('_') and iskeyword(name[:-1]): name = name[:-1]
391
+ return partial(self, name)
392
+
393
+ def __call__(self, name, /, *children, attrs_=None, **attrs):
394
+ tag = _xml_qualify(name, self._prefix)
395
+ pairs = [(_xml_qualify(_xml_keyword(k), self._attr_ns), v) for k,v in attrs.items()]
396
+ pairs += list((attrs_ or {}).items())
397
+ ns,seen,values = dict(self._ns),set(),[]
398
+ prefix, _ = _xml_name(tag)
399
+ if prefix: ns[prefix] = self._namespace(prefix)
400
+ for key, value in pairs:
401
+ if value is None: continue
402
+ prefix, local = _xml_name(key)
403
+ if prefix: ns[prefix] = self._namespace(prefix)
404
+ expanded = (ns[prefix] if prefix else '', local)
405
+ if expanded in seen: raise ValueError(f'Duplicate XML attribute: {key!r}')
406
+ seen.add(expanded)
407
+ values.append((key, _xml_value(value)))
408
+ return self._node_cls(tag, children, tuple(values), ns)
409
+
@@ -326,22 +326,17 @@ def rsync_multi(ip, files, user='ubuntu', persist='5m'):
326
326
  for src,dst in files: shell(f'rsync -az -e "ssh -S {sock}" {src} {user}@{ip}:{dst}')
327
327
 
328
328
  # %% ../nbs/03_xtras.ipynb #dda52646
329
- def run(cmd, *rest, same_in_win=False, ignore_ex=False, as_bytes=False, stderr=True):
329
+ def run(cmd, *rest, same_in_win=False, ignore_ex=False, as_bytes=False, stderr=True, inp=None):
330
330
  "Pass `cmd` (splitting with `shlex` if string) to `subprocess.run`; return `stdout`; raise `IOError` if fails"
331
- # Even the command is same on Windows, we have to add `cmd /c `"
332
331
  import subprocess
333
- if rest:
334
- if sys.platform == 'win32' and same_in_win:
335
- cmd = ('cmd', '/c', cmd, *rest)
336
- else:
337
- cmd = (cmd,)+rest
332
+ if rest: cmd = ('cmd', '/c', cmd, *rest) if sys.platform == 'win32' and same_in_win else (cmd,)+rest
338
333
  elif isinstance(cmd, str):
339
- if sys.platform == 'win32' and same_in_win: cmd = 'cmd /c ' + cmd
340
334
  import shlex
335
+ if sys.platform == 'win32' and same_in_win: cmd = 'cmd /c ' + cmd
341
336
  cmd = shlex.split(cmd)
342
- elif isinstance(cmd, list):
343
- if sys.platform == 'win32' and same_in_win: cmd = ['cmd', '/c'] + cmd
344
- res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
337
+ elif isinstance(cmd, list) and sys.platform == 'win32' and same_in_win: cmd = ['cmd', '/c'] + cmd
338
+ if isinstance(inp, str): inp = inp.encode()
339
+ res = subprocess.run(cmd, input=inp, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
345
340
  stdout = res.stdout
346
341
  if stderr and res.stderr: stdout += b' ;; ' + res.stderr
347
342
  if not as_bytes: stdout = stdout.decode().strip()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastcore
3
- Version: 2.2.23
3
+ Version: 2.2.24
4
4
  Summary: Python supercharged for fastai development
5
5
  Author-email: Jeremy Howard and Sylvain Gugger <infos@fast.ai>
6
6
  License: Apache-2.0
@@ -84,6 +84,7 @@ To install fastcore run: `conda install fastcore -c fastai` (if you use Anaconda
84
84
  - `fastcore.test`: Simple testing functions
85
85
  - `fastcore.foundation`: Mixins, delegation, composition, and more
86
86
  - `fastcore.xtras`: Utility functions to help with functional-style programming, parallel processing, and more
87
+ - `fastcore.xml`: HTML with [`ft`](https://fastcore.fast.ai/xml.html#ft), and case-preserving, namespace-aware XML with [`E`](https://fastcore.fast.ai/xml.html#e) factories
87
88
 
88
89
  To get started, we recommend you read through [the fastcore tour](https://fastcore.fast.ai/tour.html).
89
90
 
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