Skip to content

Commit 0549235

Browse files
committed
fixes #105
1 parent 72512ca commit 0549235

File tree

5 files changed

+123
-20
lines changed

5 files changed

+123
-20
lines changed

fastcore/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.0.16"
1+
__version__ = "1.0.17"

fastcore/_nbdev.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"snake2camel": "02_utils.ipynb",
6161
"class2attr": "02_utils.ipynb",
6262
"hasattrs": "02_utils.ipynb",
63+
"setattrs": "02_utils.ipynb",
6364
"ShowPrint": "02_utils.ipynb",
6465
"Int": "02_utils.ipynb",
6566
"Str": "02_utils.ipynb",
@@ -108,7 +109,7 @@
108109
"join_path_file": "02_utils.ipynb",
109110
"urlread": "02_utils.ipynb",
110111
"urljson": "02_utils.ipynb",
111-
"run_proc": "02_utils.ipynb",
112+
"run": "02_utils.ipynb",
112113
"do_request": "02_utils.ipynb",
113114
"sort_by_run": "02_utils.ipynb",
114115
"PrettyString": "02_utils.ipynb",

fastcore/utils.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/02_utils.ipynb (unless otherwise specified).
22

33
__all__ = ['ifnone', 'maybe_attr', 'basic_repr', 'get_class', 'mk_class', 'wrap_class', 'ignore_exceptions', 'dict2obj',
4-
'store_attr', 'attrdict', 'properties', 'camel2snake', 'snake2camel', 'class2attr', 'hasattrs', 'ShowPrint',
5-
'Int', 'Str', 'Float', 'tuplify', 'detuplify', 'replicate', 'uniqueify', 'setify', 'merge', 'is_listy',
6-
'range_of', 'groupby', 'last_index', 'shufflish', 'IterLen', 'ReindexCollection', 'num_methods',
4+
'store_attr', 'attrdict', 'properties', 'camel2snake', 'snake2camel', 'class2attr', 'hasattrs', 'setattrs',
5+
'ShowPrint', 'Int', 'Str', 'Float', 'tuplify', 'detuplify', 'replicate', 'uniqueify', 'setify', 'merge',
6+
'is_listy', 'range_of', 'groupby', 'last_index', 'shufflish', 'IterLen', 'ReindexCollection', 'num_methods',
77
'rnum_methods', 'inum_methods', 'fastuple', 'Inf', 'in_', 'lt', 'gt', 'le', 'ge', 'eq', 'ne', 'add', 'sub',
88
'mul', 'truediv', 'is_', 'is_not', 'in_', 'true', 'stop', 'gen', 'chunked', 'trace', 'compose', 'maps',
99
'partialler', 'mapped', 'instantiate', 'using_attr', 'Self', 'Self', 'remove_patches_path', 'bunzip',
10-
'join_path_file', 'urlread', 'urljson', 'run_proc', 'do_request', 'sort_by_run', 'PrettyString',
11-
'round_multiple', 'even_mults', 'num_cpus', 'add_props', 'ContextManagers', 'set_num_threads',
12-
'ProcessPoolExecutor', 'ThreadPoolExecutor', 'parallel', 'run_procs', 'parallel_gen']
10+
'join_path_file', 'urlread', 'urljson', 'run', 'do_request', 'sort_by_run', 'PrettyString', 'round_multiple',
11+
'even_mults', 'num_cpus', 'add_props', 'ContextManagers', 'set_num_threads', 'ProcessPoolExecutor',
12+
'ThreadPoolExecutor', 'parallel', 'run_procs', 'parallel_gen']
1313

1414
# Cell
1515
from .imports import *
1616
from .foundation import *
1717
from functools import wraps
1818

19-
import mimetypes,bz2,pickle,random,json,urllib,subprocess
19+
import mimetypes,bz2,pickle,random,json,urllib,subprocess,shlex
2020
from contextlib import contextmanager
2121
from urllib.request import Request,urlopen
2222
from urllib.error import HTTPError
@@ -148,6 +148,12 @@ def hasattrs(o,attrs):
148148
"Test whether `o` contains all `attrs`"
149149
return all(hasattr(o,attr) for attr in attrs)
150150

151+
# Cell
152+
def setattrs(dest, flds, src):
153+
f = dict.get if isinstance(src, dict) else getattr
154+
flds = re.split(r",\s*", flds)
155+
for fld in flds: setattr(dest, fld, f(src, fld))
156+
151157
# Cell
152158
#hide
153159
class ShowPrint:
@@ -563,9 +569,11 @@ def urljson(url):
563569
return json.loads(urlread(url))
564570

565571
# Cell
566-
def run_proc(*args):
567-
"Pass `args` to `subprocess.run`, returning `stdout`, or raise `IOError` on failure"
568-
res = subprocess.run(args, capture_output=True)
572+
def run(cmd, *rest):
573+
"Pass `cmd` (splitting with `shlex` if string) to `subprocess.run`, returning `stdout`, or raise `IOError` on failure"
574+
if rest: cmd = (cmd,)+rest
575+
elif isinstance(cmd,str): cmd = shlex.split(cmd)
576+
res = subprocess.run(cmd, capture_output=True)
569577
if res.returncode: raise IOError("{} ;; {}".format(res.stdout, res.stderr))
570578
return res.stdout
571579

nbs/02_utils.ipynb

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"from fastcore.foundation import *\n",
2121
"from functools import wraps\n",
2222
"\n",
23-
"import mimetypes,bz2,pickle,random,json,urllib,subprocess\n",
23+
"import mimetypes,bz2,pickle,random,json,urllib,subprocess,shlex\n",
2424
"from contextlib import contextmanager\n",
2525
"from urllib.request import Request,urlopen\n",
2626
"from urllib.error import HTTPError\n",
@@ -294,7 +294,7 @@
294294
{
295295
"data": {
296296
"text/plain": [
297-
"<__main__._t at 0x7f756accd110>"
297+
"<__main__._t at 0x7efc6c1ec150>"
298298
]
299299
},
300300
"execution_count": null,
@@ -989,6 +989,45 @@
989989
"assert not hasattrs(1,('imag','foo'))"
990990
]
991991
},
992+
{
993+
"cell_type": "code",
994+
"execution_count": null,
995+
"metadata": {},
996+
"outputs": [],
997+
"source": [
998+
"#export\n",
999+
"def setattrs(dest, flds, src):\n",
1000+
" f = dict.get if isinstance(src, dict) else getattr\n",
1001+
" flds = re.split(r\",\\s*\", flds)\n",
1002+
" for fld in flds: setattr(dest, fld, f(src, fld))"
1003+
]
1004+
},
1005+
{
1006+
"cell_type": "code",
1007+
"execution_count": null,
1008+
"metadata": {},
1009+
"outputs": [],
1010+
"source": [
1011+
"d = dict(a=1,bb=\"2\",ignore=3)\n",
1012+
"o = SimpleNamespace()\n",
1013+
"setattrs(o, \"a,bb\", d)\n",
1014+
"test_eq(o.a, 1)\n",
1015+
"test_eq(o.bb, \"2\")"
1016+
]
1017+
},
1018+
{
1019+
"cell_type": "code",
1020+
"execution_count": null,
1021+
"metadata": {},
1022+
"outputs": [],
1023+
"source": [
1024+
"d = SimpleNamespace(a=1,bb=\"2\",ignore=3)\n",
1025+
"o = SimpleNamespace()\n",
1026+
"setattrs(o, \"a,bb\", d)\n",
1027+
"test_eq(o.a, 1)\n",
1028+
"test_eq(o.bb, \"2\")"
1029+
]
1030+
},
9921031
{
9931032
"cell_type": "markdown",
9941033
"metadata": {},
@@ -1668,7 +1707,7 @@
16681707
{
16691708
"data": {
16701709
"text/plain": [
1671-
"['c', 'a', 'f', 'e', 'g', 'h', 'd', 'b']"
1710+
"['f', 'g', 'e', 'd', 'b', 'a', 'h', 'c']"
16721711
]
16731712
},
16741713
"execution_count": null,
@@ -3089,11 +3128,66 @@
30893128
"outputs": [],
30903129
"source": [
30913130
"#export\n",
3092-
"def run_proc(*args):\n",
3093-
" \"Pass `args` to `subprocess.run`, returning `stdout`, or raise `IOError` on failure\"\n",
3094-
" res = subprocess.run(args, capture_output=True)\n",
3131+
"def run(cmd, *rest, ignore_ex=False, as_bytes=False):\n",
3132+
" \"Pass `cmd` (splitting with `shlex` if string) to `subprocess.run`, returning `stdout`, or raise `IOError` on failure\"\n",
3133+
" if rest: cmd = (cmd,)+rest\n",
3134+
" elif isinstance(cmd,str): cmd = shlex.split(cmd)\n",
3135+
" res = subprocess.run(cmd, capture_output=True)\n",
3136+
" stdout = res.stdout\n",
3137+
" if not as_bytes: stdout = stdout.decode()\n",
3138+
" if ignore_ex: return (res.returncode, stdout)\n",
30953139
" if res.returncode: raise IOError(\"{} ;; {}\".format(res.stdout, res.stderr))\n",
3096-
" return res.stdout"
3140+
" return stdout"
3141+
]
3142+
},
3143+
{
3144+
"cell_type": "markdown",
3145+
"metadata": {},
3146+
"source": [
3147+
"You can pass a string (which will be split based on standard shell rules), a list, or pass args directly:"
3148+
]
3149+
},
3150+
{
3151+
"cell_type": "code",
3152+
"execution_count": null,
3153+
"metadata": {},
3154+
"outputs": [],
3155+
"source": [
3156+
"assert 'ipynb' in run('ls -l')\n",
3157+
"assert 'ipynb' in run(['ls', '-l'])\n",
3158+
"assert 'ipynb' in run('ls', '-l')"
3159+
]
3160+
},
3161+
{
3162+
"cell_type": "markdown",
3163+
"metadata": {},
3164+
"source": [
3165+
"Some commands fail in non-error situations, like `grep`. Use `ignore_ex` in those cases, which will return a tuple of stdout and returncode:"
3166+
]
3167+
},
3168+
{
3169+
"cell_type": "code",
3170+
"execution_count": null,
3171+
"metadata": {},
3172+
"outputs": [],
3173+
"source": [
3174+
"test_eq(run('grep asdfds 00_test.ipynb', ignore_ex=True)[0], 1)"
3175+
]
3176+
},
3177+
{
3178+
"cell_type": "markdown",
3179+
"metadata": {},
3180+
"source": [
3181+
"`run` automatically decodes returned bytes to a `str`. Use `as_bytes` to skip that:"
3182+
]
3183+
},
3184+
{
3185+
"cell_type": "code",
3186+
"execution_count": null,
3187+
"metadata": {},
3188+
"outputs": [],
3189+
"source": [
3190+
"test_eq(run('echo hi', as_bytes=True), b'hi\\n')"
30973191
]
30983192
},
30993193
{

settings.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ author = Jeremy Howard and Sylvain Gugger
77
author_email = [email protected]
88
copyright = fast.ai
99
branch = master
10-
version = 1.0.16
10+
version = 1.0.17
1111
min_python = 3.6
1212
audience = Developers
1313
language = English

0 commit comments

Comments
 (0)