Skip to content

Commit 1388c91

Browse files
committed
fixes #692
1 parent da566b2 commit 1388c91

File tree

3 files changed

+111
-26
lines changed

3 files changed

+111
-26
lines changed

fastcore/_modidx.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,4 +655,5 @@
655655
'fastcore.xtras.type2str': ('xtras.html#type2str', 'fastcore/xtras.py'),
656656
'fastcore.xtras.untar_dir': ('xtras.html#untar_dir', 'fastcore/xtras.py'),
657657
'fastcore.xtras.utc2local': ('xtras.html#utc2local', 'fastcore/xtras.py'),
658+
'fastcore.xtras.vars_pub': ('xtras.html#vars_pub', 'fastcore/xtras.py'),
658659
'fastcore.xtras.walk': ('xtras.html#walk', 'fastcore/xtras.py')}}}

fastcore/xtras.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
'truncstr', 'sparkline', 'modify_exception', 'round_multiple', 'set_num_threads', 'join_path_file',
1414
'autostart', 'EventTimer', 'stringfmt_names', 'PartialFormatter', 'partial_format', 'utc2local', 'local2utc',
1515
'trace', 'modified_env', 'ContextManagers', 'shufflish', 'console_help', 'hl_md', 'type2str',
16-
'dataclass_src', 'Unset', 'nullable_dc', 'make_nullable', 'flexiclass', 'asdict', 'is_typeddict',
16+
'dataclass_src', 'Unset', 'nullable_dc', 'make_nullable', 'flexiclass', 'asdict', 'vars_pub', 'is_typeddict',
1717
'is_namedtuple', 'CachedIter', 'CachedAwaitable', 'reawaitable', 'flexicache', 'time_policy', 'mtime_policy',
1818
'timed_cache']
1919

@@ -770,9 +770,16 @@ def asdict(o)->dict:
770770
elif hasattr(o, '__iter__'):
771771
try: r = dict(o)
772772
except TypeError: pass
773-
elif hasattr(o, '__dict__'): r = o.__dict__
774-
else: raise TypeError(f'Can not convert {o} to a dict')
775-
return {k:v for k,v in r.items() if v not in (UNSET,MISSING)}
773+
elif hasattr(o, '__flds__'): r = {x:getattr(o,x) for x in o.__flds__}
774+
else: r = vars(o)
775+
skip = set(getattr(o, '__skip__', []))
776+
return {k:v for k,v in r.items() if v is not UNSET and v is not MISSING and k not in skip}
777+
778+
# %% ../nbs/03_xtras.ipynb
779+
def vars_pub(x):
780+
"Get public non-skipped vars"
781+
skip = set(getattr(x, '__skip__', []))
782+
return [o for o in vars(x) if o[0]!='_' and o not in skip]
776783

777784
# %% ../nbs/03_xtras.ipynb
778785
def is_typeddict(cls:type)->bool:

nbs/03_xtras.ipynb

Lines changed: 99 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,7 @@
688688
{
689689
"data": {
690690
"text/plain": [
691-
"'pip 25.1.1 from /Users/jhoward/aai-ws/.venv/lib/python3.12/site-packages/pip (python 3.12)'"
691+
"'pip 25.2 from /Users/jhoward/aai-ws/.venv/lib/python3.12/site-packages/pip (python 3.12)'"
692692
]
693693
},
694694
"execution_count": null,
@@ -1441,7 +1441,7 @@
14411441
"text/markdown": [
14421442
"---\n",
14431443
"\n",
1444-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L389){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
1444+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L391){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
14451445
"\n",
14461446
"#### ReindexCollection\n",
14471447
"\n",
@@ -1452,7 +1452,7 @@
14521452
"text/plain": [
14531453
"---\n",
14541454
"\n",
1455-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L389){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
1455+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L391){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
14561456
"\n",
14571457
"#### ReindexCollection\n",
14581458
"\n",
@@ -1524,7 +1524,7 @@
15241524
"text/markdown": [
15251525
"---\n",
15261526
"\n",
1527-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L400){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
1527+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L402){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
15281528
"\n",
15291529
"###### ReindexCollection.reindex\n",
15301530
"\n",
@@ -1535,7 +1535,7 @@
15351535
"text/plain": [
15361536
"---\n",
15371537
"\n",
1538-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L400){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
1538+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L402){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
15391539
"\n",
15401540
"###### ReindexCollection.reindex\n",
15411541
"\n",
@@ -1624,7 +1624,7 @@
16241624
"text/markdown": [
16251625
"---\n",
16261626
"\n",
1627-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L404){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
1627+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L406){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
16281628
"\n",
16291629
"##### ReindexCollection.cache_clear\n",
16301630
"\n",
@@ -1635,7 +1635,7 @@
16351635
"text/plain": [
16361636
"---\n",
16371637
"\n",
1638-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L404){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
1638+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L406){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
16391639
"\n",
16401640
"##### ReindexCollection.cache_clear\n",
16411641
"\n",
@@ -1689,7 +1689,7 @@
16891689
"text/markdown": [
16901690
"---\n",
16911691
"\n",
1692-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L401){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
1692+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L403){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
16931693
"\n",
16941694
"##### ReindexCollection.shuffle\n",
16951695
"\n",
@@ -1700,7 +1700,7 @@
17001700
"text/plain": [
17011701
"---\n",
17021702
"\n",
1703-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L401){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
1703+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L403){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
17041704
"\n",
17051705
"##### ReindexCollection.shuffle\n",
17061706
"\n",
@@ -1733,7 +1733,7 @@
17331733
{
17341734
"data": {
17351735
"text/plain": [
1736-
"['g', 'c', 'f', 'd', 'a', 'b', 'e', 'h']"
1736+
"['f', 'b', 'd', 'e', 'g', 'a', 'c', 'h']"
17371737
]
17381738
},
17391739
"execution_count": null,
@@ -2274,7 +2274,7 @@
22742274
{
22752275
"data": {
22762276
"text/plain": [
2277-
"'https://github.com/fastai/fastcore/tree/master/fastcore/test.py#L37'"
2277+
"'https://github.com/fastai/fastcore/tree/master/fastcore/test.py#L38'"
22782278
]
22792279
},
22802280
"execution_count": null,
@@ -2577,7 +2577,7 @@
25772577
"text/markdown": [
25782578
"---\n",
25792579
"\n",
2580-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L532){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
2580+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L573){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
25812581
"\n",
25822582
"#### EventTimer\n",
25832583
"\n",
@@ -2588,7 +2588,7 @@
25882588
"text/plain": [
25892589
"---\n",
25902590
"\n",
2591-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L532){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
2591+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L573){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
25922592
"\n",
25932593
"#### EventTimer\n",
25942594
"\n",
@@ -2622,8 +2622,8 @@
26222622
"name": "stdout",
26232623
"output_type": "stream",
26242624
"text": [
2625-
"Num Events: 13, Freq/sec: 451.7\n",
2626-
"Most recent: ▁▁▁▂▇ 264.5 257.0 278.7 293.7 363.0\n"
2625+
"Num Events: 1, Freq/sec: 161.2\n",
2626+
"Most recent: ▁▇▆▁▃ 59.3 132.1 120.0 58.9 92.0\n"
26272627
]
26282628
}
26292629
],
@@ -2702,7 +2702,7 @@
27022702
"text/markdown": [
27032703
"---\n",
27042704
"\n",
2705-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L564){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
2705+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L605){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
27062706
"\n",
27072707
"#### PartialFormatter\n",
27082708
"\n",
@@ -2713,7 +2713,7 @@
27132713
"text/plain": [
27142714
"---\n",
27152715
"\n",
2716-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L564){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
2716+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L605){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
27172717
"\n",
27182718
"#### PartialFormatter\n",
27192719
"\n",
@@ -2918,7 +2918,7 @@
29182918
"text/markdown": [
29192919
"---\n",
29202920
"\n",
2921-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L621){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
2921+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L662){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
29222922
"\n",
29232923
"#### ContextManagers\n",
29242924
"\n",
@@ -2929,7 +2929,7 @@
29292929
"text/plain": [
29302930
"---\n",
29312931
"\n",
2932-
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L621){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
2932+
"[source](https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py#L662){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
29332933
"\n",
29342934
"#### ContextManagers\n",
29352935
"\n",
@@ -3362,9 +3362,10 @@
33623362
" elif hasattr(o, '__iter__'):\n",
33633363
" try: r = dict(o)\n",
33643364
" except TypeError: pass\n",
3365-
" elif hasattr(o, '__dict__'): r = o.__dict__\n",
3366-
" else: raise TypeError(f'Can not convert {o} to a dict')\n",
3367-
" return {k:v for k,v in r.items() if v not in (UNSET,MISSING)}"
3365+
" elif hasattr(o, '__flds__'): r = {x:getattr(o,x) for x in o.__flds__}\n",
3366+
" else: r = vars(o)\n",
3367+
" skip = set(getattr(o, '__skip__', []))\n",
3368+
" return {k:v for k,v in r.items() if v is not UNSET and v is not MISSING and k not in skip}"
33683369
]
33693370
},
33703371
{
@@ -3394,13 +3395,89 @@
33943395
"asdict(bob)"
33953396
]
33963397
},
3398+
{
3399+
"cell_type": "markdown",
3400+
"metadata": {},
3401+
"source": [
3402+
"Set the optional `__flds__` parameter to customise the field list, and the optional `__skip__` parameter to skip some names."
3403+
]
3404+
},
3405+
{
3406+
"cell_type": "code",
3407+
"execution_count": null,
3408+
"metadata": {},
3409+
"outputs": [],
3410+
"source": [
3411+
"class CustomObj:\n",
3412+
" def __init__(self): self.a,self.b,self.c,self.d = 1,2,3,4\n",
3413+
" __flds__ = ['a','b','c','d']\n",
3414+
" __skip__ = ['b']\n",
3415+
"\n",
3416+
"obj = CustomObj()\n",
3417+
"test_eq(asdict(obj), {'a': 1, 'c': 3, 'd': 4})"
3418+
]
3419+
},
33973420
{
33983421
"cell_type": "markdown",
33993422
"metadata": {},
34003423
"source": [
34013424
"To customise dict conversion behavior for a class, implement the `_asdict` method (this is used in the Python stdlib for named tuples)."
34023425
]
34033426
},
3427+
{
3428+
"cell_type": "code",
3429+
"execution_count": null,
3430+
"metadata": {},
3431+
"outputs": [],
3432+
"source": [
3433+
"#| export\n",
3434+
"def vars_pub(x):\n",
3435+
" \"Get public non-skipped vars\"\n",
3436+
" skip = set(getattr(x, '__skip__', []))\n",
3437+
" return [o for o in vars(x) if o[0]!='_' and o not in skip]"
3438+
]
3439+
},
3440+
{
3441+
"cell_type": "markdown",
3442+
"metadata": {},
3443+
"source": [
3444+
"The `vars_pub` function returns a list of public (non-underscore-prefixed) variable names from an object, excluding any names listed in the object's optional `__skip__` attribute."
3445+
]
3446+
},
3447+
{
3448+
"cell_type": "code",
3449+
"execution_count": null,
3450+
"metadata": {},
3451+
"outputs": [],
3452+
"source": [
3453+
"class TestObj:\n",
3454+
" def __init__(self): self.pub_attr,self._priv_attr,self.another_pub,self.skip_me = 1,2,3,4\n",
3455+
" __skip__ = ['skip_me']\n",
3456+
"\n",
3457+
"obj = TestObj()\n",
3458+
"test_eq(vars_pub(obj), ['pub_attr', 'another_pub'])"
3459+
]
3460+
},
3461+
{
3462+
"cell_type": "markdown",
3463+
"metadata": {},
3464+
"source": [
3465+
"Without `__skip__`, all pub vars are returned"
3466+
]
3467+
},
3468+
{
3469+
"cell_type": "code",
3470+
"execution_count": null,
3471+
"metadata": {},
3472+
"outputs": [],
3473+
"source": [
3474+
"class SimpleObj:\n",
3475+
" def __init__(self): self.a,self._b,self.c = 1,2,3\n",
3476+
"\n",
3477+
"simple = SimpleObj()\n",
3478+
"test_eq(vars_pub(simple), ['a', 'c'])"
3479+
]
3480+
},
34043481
{
34053482
"cell_type": "code",
34063483
"execution_count": null,

0 commit comments

Comments
 (0)