fastcore 2.2.6__tar.gz → 2.2.7__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 (44) hide show
  1. {fastcore-2.2.6/fastcore.egg-info → fastcore-2.2.7}/PKG-INFO +1 -1
  2. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/__init__.py +1 -1
  3. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/_modidx.py +4 -1
  4. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/basics.py +10 -5
  5. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/meta.py +5 -7
  6. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/nbio.py +29 -10
  7. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/xtras.py +12 -2
  8. {fastcore-2.2.6 → fastcore-2.2.7/fastcore.egg-info}/PKG-INFO +1 -1
  9. {fastcore-2.2.6 → fastcore-2.2.7}/CONTRIBUTING.md +0 -0
  10. {fastcore-2.2.6 → fastcore-2.2.7}/LICENSE +0 -0
  11. {fastcore-2.2.6 → fastcore-2.2.7}/MANIFEST.in +0 -0
  12. {fastcore-2.2.6 → fastcore-2.2.7}/README.md +0 -0
  13. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/aio.py +0 -0
  14. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/all.py +0 -0
  15. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/ansi.py +0 -0
  16. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/apisurface.py +0 -0
  17. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/dispatch.py +0 -0
  18. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/docments.py +0 -0
  19. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/docscrape.py +0 -0
  20. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/editskill.py +0 -0
  21. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/foundation.py +0 -0
  22. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/funccall.py +0 -0
  23. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/imghdr.py +0 -0
  24. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/imports.py +0 -0
  25. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/nb_imports.py +0 -0
  26. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/net.py +0 -0
  27. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/parallel.py +0 -0
  28. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/py2pyi.py +0 -0
  29. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/script.py +0 -0
  30. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/shutil.py +0 -0
  31. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/style.py +0 -0
  32. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/test.py +0 -0
  33. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/tools.py +0 -0
  34. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/transform.py +0 -0
  35. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/utils.py +0 -0
  36. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/xdg.py +0 -0
  37. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore/xml.py +0 -0
  38. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore.egg-info/SOURCES.txt +0 -0
  39. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore.egg-info/dependency_links.txt +0 -0
  40. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore.egg-info/entry_points.txt +0 -0
  41. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore.egg-info/requires.txt +0 -0
  42. {fastcore-2.2.6 → fastcore-2.2.7}/fastcore.egg-info/top_level.txt +0 -0
  43. {fastcore-2.2.6 → fastcore-2.2.7}/pyproject.toml +0 -0
  44. {fastcore-2.2.6 → fastcore-2.2.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastcore
3
- Version: 2.2.6
3
+ Version: 2.2.7
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
@@ -55,4 +55,4 @@ Modules:
55
55
  - `fastcore.xml`: Concise generation of XML.
56
56
  - `fastcore.xtras`: Utility functions used in the fastai library"""
57
57
 
58
- __version__ = "2.2.6"
58
+ __version__ = "2.2.7"
@@ -217,6 +217,7 @@ d = { 'settings': { 'branch': 'main',
217
217
  'fastcore.basics.instantiate': ('basics.html#instantiate', 'fastcore/basics.py'),
218
218
  'fastcore.basics.is_array': ('basics.html#is_array', 'fastcore/basics.py'),
219
219
  'fastcore.basics.joins': ('basics.html#joins', 'fastcore/basics.py'),
220
+ 'fastcore.basics.kindsort': ('basics.html#kindsort', 'fastcore/basics.py'),
220
221
  'fastcore.basics.last': ('basics.html#last', 'fastcore/basics.py'),
221
222
  'fastcore.basics.last_index': ('basics.html#last_index', 'fastcore/basics.py'),
222
223
  'fastcore.basics.last_match': ('basics.html#last_match', 'fastcore/basics.py'),
@@ -588,7 +589,6 @@ d = { 'settings': { 'branch': 'main',
588
589
  'fastcore.nbio._directives_set': ('nbio.html#_directives_set', 'fastcore/nbio.py'),
589
590
  'fastcore.nbio._is_exported': ('nbio.html#_is_exported', 'fastcore/nbio.py'),
590
591
  'fastcore.nbio._is_json_mime': ('nbio.html#_is_json_mime', 'fastcore/nbio.py'),
591
- 'fastcore.nbio._is_noeval': ('nbio.html#_is_noeval', 'fastcore/nbio.py'),
592
592
  'fastcore.nbio._is_text': ('nbio.html#_is_text', 'fastcore/nbio.py'),
593
593
  'fastcore.nbio._meta_directives': ('nbio.html#_meta_directives', 'fastcore/nbio.py'),
594
594
  'fastcore.nbio._mkout': ('nbio.html#_mkout', 'fastcore/nbio.py'),
@@ -611,9 +611,11 @@ d = { 'settings': { 'branch': 'main',
611
611
  'fastcore.nbio.deep_merge': ('nbio.html#deep_merge', 'fastcore/nbio.py'),
612
612
  'fastcore.nbio.dict2nb': ('nbio.html#dict2nb', 'fastcore/nbio.py'),
613
613
  'fastcore.nbio.dir_tag': ('nbio.html#dir_tag', 'fastcore/nbio.py'),
614
+ 'fastcore.nbio.does_cell_eval': ('nbio.html#does_cell_eval', 'fastcore/nbio.py'),
614
615
  'fastcore.nbio.find_cells': ('nbio.html#find_cells', 'fastcore/nbio.py'),
615
616
  'fastcore.nbio.find_id': ('nbio.html#find_id', 'fastcore/nbio.py'),
616
617
  'fastcore.nbio.first_code_ln': ('nbio.html#first_code_ln', 'fastcore/nbio.py'),
618
+ 'fastcore.nbio.fm_default_eval': ('nbio.html#fm_default_eval', 'fastcore/nbio.py'),
617
619
  'fastcore.nbio.item2xml': ('nbio.html#item2xml', 'fastcore/nbio.py'),
618
620
  'fastcore.nbio.join_out': ('nbio.html#join_out', 'fastcore/nbio.py'),
619
621
  'fastcore.nbio.md_frontmatter': ('nbio.html#md_frontmatter', 'fastcore/nbio.py'),
@@ -974,6 +976,7 @@ d = { 'settings': { 'branch': 'main',
974
976
  'fastcore.xtras.ssh': ('xtras.html#ssh', 'fastcore/xtras.py'),
975
977
  'fastcore.xtras.str_diff': ('xtras.html#str_diff', 'fastcore/xtras.py'),
976
978
  'fastcore.xtras.stringfmt_names': ('xtras.html#stringfmt_names', 'fastcore/xtras.py'),
979
+ 'fastcore.xtras.strloader': ('xtras.html#strloader', 'fastcore/xtras.py'),
977
980
  'fastcore.xtras.take_lines': ('xtras.html#take_lines', 'fastcore/xtras.py'),
978
981
  'fastcore.xtras.time_policy': ('xtras.html#time_policy', 'fastcore/xtras.py'),
979
982
  'fastcore.xtras.timed_cache': ('xtras.html#timed_cache', 'fastcore/xtras.py'),
@@ -55,9 +55,9 @@ __all__ = ['defaults', 'null', 'num_methods', 'rnum_methods', 'inum_methods', 'a
55
55
  'using_attr', 'negate', 'fail_clean', 'dstar', 'copy_func', 'patch_to', 'patch', 'extend_enum', 'compile_re',
56
56
  'ImportEnum', 'StrEnum', 'str_enum', 'ValEnum', 'Stateful', 'NotStr', 'PrettyString', 'even_mults',
57
57
  'num_cpus', 'add_props', 'str2bool', 'str2int', 'str2float', 'str2list', 'str2date', 'str2dt', 'to_bool',
58
- 'to_int', 'to_float', 'to_list', 'to_date', 'typed', 'exec_new', 'exec_import', 'sig_with_params',
59
- 'fdelegates', 'xdumps', 'revive_dates', 'lt', 'gt', 'le', 'ge', 'eq', 'ne', 'add', 'sub', 'mul', 'truediv',
60
- 'is_', 'is_not', 'mod']
58
+ 'to_int', 'to_float', 'to_list', 'to_date', 'typed', 'exec_new', 'exec_import', 'kindsort',
59
+ 'sig_with_params', 'fdelegates', 'xdumps', 'revive_dates', 'lt', 'gt', 'le', 'ge', 'eq', 'ne', 'add', 'sub',
60
+ 'mul', 'truediv', 'is_', 'is_not', 'mod']
61
61
 
62
62
  # %% ../nbs/01_basics.ipynb #0e91ed82
63
63
  from .imports import *
@@ -1430,13 +1430,18 @@ def exec_import(mod, sym):
1430
1430
  return exec_new(f'from {mod} import {sym}')
1431
1431
 
1432
1432
  # %% ../nbs/01_basics.ipynb #bf300cb0
1433
+ def kindsort(ps):
1434
+ "Params sorted by `Parameter.kind`"
1435
+ return sorted(ps, key=lambda p: p.kind)
1436
+
1433
1437
  def sig_with_params(sig, remove=None, keep=None, **updates):
1438
+ "Copy of `sig` with `remove`d params dropped, `keep` params retained, and `updates` added, kind-sorted"
1434
1439
  sigd = dict(sig.parameters)
1435
1440
  for k in listify(remove): sigd.pop(k, None)
1436
1441
  kept = {k: sigd.pop(k) for k in listify(keep) if k in sigd}
1437
1442
  sigd.update(updates)
1438
- sigd.update(kept) # re-add at end
1439
- return sig.replace(parameters=sigd.values())
1443
+ sigd.update(kept)
1444
+ return sig.replace(parameters=kindsort(sigd.values()))
1440
1445
 
1441
1446
  # %% ../nbs/01_basics.ipynb #b5758ba3
1442
1447
  def fdelegates(to):
@@ -108,11 +108,10 @@ def use_kwargs_dict(keep=False, **kwargs):
108
108
  def _f(f):
109
109
  sig = inspect.signature(f)
110
110
  sigd = dict(sig.parameters)
111
- k = sigd.pop('kwargs')
111
+ if not keep: sigd.pop('kwargs')
112
112
  s2 = {n:_mk_param(n,d) for n,d in kwargs.items() if n not in sigd}
113
113
  sigd.update(s2)
114
- if keep: sigd['kwargs'] = k
115
- f.__signature__ = sig.replace(parameters=sigd.values())
114
+ f.__signature__ = sig.replace(parameters=kindsort(sigd.values()))
116
115
  return f
117
116
  return _f
118
117
 
@@ -122,11 +121,10 @@ def use_kwargs(names, keep=False):
122
121
  def _f(f):
123
122
  sig = inspect.signature(f)
124
123
  sigd = dict(sig.parameters)
125
- k = sigd.pop('kwargs')
124
+ if not keep: sigd.pop('kwargs')
126
125
  s2 = {n:_mk_param(n) for n in names if n not in sigd}
127
126
  sigd.update(s2)
128
- if keep: sigd['kwargs'] = k
129
- f.__signature__ = sig.replace(parameters=sigd.values())
127
+ f.__signature__ = sig.replace(parameters=kindsort(sigd.values()))
130
128
  return f
131
129
  return _f
132
130
 
@@ -245,7 +243,7 @@ def splice_sig(wrapper, fn, *skips):
245
243
  split = next((i for i, p in enumerate(w_ps) if p.kind==Parameter.VAR_POSITIONAL), len(w_ps))
246
244
  pre = w_ps[:split]
247
245
  post = [p for p in w_ps[split+1:] if p.kind != Parameter.VAR_KEYWORD]
248
- sig = signature(wrapper).replace(parameters=[*pre, *f_ps, *post])
246
+ sig = signature(wrapper).replace(parameters=kindsort([*pre, *f_ps, *post]))
249
247
  wrapper = wraps(fn)(wrapper)
250
248
  wrapper.__signature__ = sig # after `wraps`, which would clobber it if `fn` has its own `__signature__`
251
249
  return wrapper
@@ -16,7 +16,7 @@ __all__ = ['langs', 'cell_insert_line', 'cell_str_replace', 'cell_strs_replace',
16
16
  'preferred_out', 'join_out', 'mk_stream', 'mk_result', 'mk_display', 'mk_error', 'concat_streams',
17
17
  'preferred_msg_out', 'render_output', 'render_outputs', 'render_text', 'item2xml', 'cell2xml', 'cells2xml',
18
18
  'Notebook', 'CellRow', 'CellRows', 'summary_nb', 'Found', 'FoundCells', 'find_cells', 'deep_merge',
19
- 'update_cell', 'select_cells', 'run_cell', 'msg2out', 'msgs2outs']
19
+ 'update_cell', 'fm_default_eval', 'does_cell_eval', 'select_cells', 'run_cell', 'msg2out', 'msgs2outs']
20
20
 
21
21
  # %% ../nbs/13_nbio.ipynb #954ca1aa
22
22
  from .basics import *
@@ -146,7 +146,7 @@ def cell_frontmatter(s:str, strvals:bool=False):
146
146
  d,body = frontmatter(s.strip(), strvals=strvals)
147
147
  return d if not body.strip() else {}
148
148
 
149
- def md_frontmatter(s:str):
149
+ def md_frontmatter(s:str, strvals:bool=False):
150
150
  "Frontmatter synthesized from an H1-formatted markdown cell: `# title`, `> description`, and `- key: value` lines"
151
151
  import yaml
152
152
  m = re.search(r'^#\s+(\S.*?)\s*$', s, flags=re.MULTILINE)
@@ -156,15 +156,16 @@ def md_frontmatter(s:str):
156
156
  if m: res['description'] = m.group(1)
157
157
  r = re.findall(r'^-\s+(\S.*:.*\S)\s*$', s, flags=re.MULTILINE)
158
158
  if r:
159
- try: res.update(yaml.safe_load('\n'.join(r)))
159
+ try: res.update(frontmatter('---\n' + '\n'.join(r) + '\n---\n', strvals=strvals)[0])
160
160
  except yaml.YAMLError as e: warn(f'Failed to create YAML dict for:\n{r}\n\n{e}\n')
161
161
  return res
162
162
 
163
163
  def nb_frontmatter(nb, strvals:bool=False):
164
- "Frontmatter from `nb`: its first raw cell plus first markdown cell (literal `---` block, or `# title` synthesis), raw keys winning"
164
+ "Frontmatter from `nb`, merged lowest-to-highest from its `metadata.nbdev` mapping (values verbatim), first markdown cell (literal `---` block, or `# title` synthesis), and first raw cell anywhere"
165
165
  raw = first(c for c in nb.cells if c.cell_type=='raw')
166
166
  md = first(c for c in nb.cells if c.cell_type=='markdown')
167
- res = (cell_frontmatter(md.source, strvals=strvals) or md_frontmatter(md.source)) if md else {}
167
+ res = dict((getattr(nb, 'metadata', None) or {}).get('nbdev') or {})
168
+ if md: res.update(cell_frontmatter(md.source, strvals=strvals) or md_frontmatter(md.source, strvals=strvals))
168
169
  if raw: res.update(cell_frontmatter(raw.source, strvals=strvals))
169
170
  return res
170
171
 
@@ -840,8 +841,21 @@ def update_cell(
840
841
 
841
842
  # %% ../nbs/13_nbio.ipynb #4db01b15
842
843
  def _is_exported(cell): return cell.has_directive('export') or cell.has_directive('exports')
843
- def _is_noeval(cell):
844
- return 'nbdev_export'+'(' in cell.source or (cell.directive('eval') or '').lower()=='false'
844
+
845
+ def fm_default_eval(fm, default_eval:bool=True):
846
+ "Participation default for cells without their own `eval` directive: the notebook-level `eval` directive in frontmatter mapping `fm` if given, else `default_eval`"
847
+ v = fm.get('eval')
848
+ if v in (True,'true','True'): return True
849
+ if v in (False,'false','False'): return False
850
+ return default_eval
851
+
852
+ def does_cell_eval(cell, default:bool):
853
+ "Does `cell` participate in a non-interactive run? Decided by its `eval` directive, or `default` when the directive is absent or unrecognized"
854
+ if 'nbdev_export'+'(' in cell.source: return False
855
+ v = cell.directive('eval')
856
+ if v in ('','true','True'): return True # '' covers bare `#| eval` and `eval: true`, which parse identically
857
+ if v in ('false','False'): return False
858
+ return default
845
859
 
846
860
  def select_cells(
847
861
  nb, # A notebook read with `read_nb`
@@ -850,20 +864,25 @@ def select_cells(
850
864
  below:bool=False, # Include each matched cell and all cells below it?
851
865
  all:bool=False, # Include all code cells (ignores `msgids`)?
852
866
  exported:bool=False, # Only cells with `#| export` or `#| exports`?
853
- skip_noeval:bool=False # Skip `#| eval: false` and `nbdev_export` cells (like `nbdev-test`)?
867
+ default_eval:bool=True, # Participation default when neither the cell nor the notebook has an `eval` directive
868
+ ignore_eval:bool=False # Skip `eval` filtering entirely: every selected cell runs
854
869
  ):
855
- "Select code cells from `nb` by cell id or unique prefix, in the order given"
870
+ "Select code cells from `nb` by cell id or unique prefix, in the order given; cells named in `msgids` always run, others follow the `eval` cascade"
856
871
  cells = [o for o in nb.cells if o.cell_type=='code']
872
+ anchors = set()
857
873
  def _one(msgid):
858
874
  idxs = [i for i,o in enumerate(cells) if str(o.id).startswith(msgid)]
859
875
  if not idxs: raise ValueError(f'No code cell id starting with {msgid!r}')
860
876
  if len(idxs)>1: raise ValueError(f'Multiple code cell ids start with {msgid!r}: {", ".join(str(cells[i].id) for i in idxs)}')
861
877
  idx = idxs[0]
878
+ anchors.add(cells[idx].id)
862
879
  return cells[:idx+1] if above else cells[idx:] if below else [cells[idx]]
863
880
  if not all and not msgids: raise ValueError('`msgids` required unless `all=True`')
864
881
  sel = cells if all else [c for m in msgids for c in _one(m)]
865
882
  if exported: sel = [o for o in sel if _is_exported(o)]
866
- if skip_noeval: sel = [o for o in sel if not _is_noeval(o)]
883
+ if not ignore_eval:
884
+ dflt = fm_default_eval(nb_frontmatter(nb), default_eval)
885
+ sel = [o for o in sel if o.id in anchors or does_cell_eval(o, dflt)]
867
886
  return sel
868
887
 
869
888
  # %% ../nbs/13_nbio.ipynb #4c71f592
@@ -26,7 +26,7 @@ __all__ = ['UNSET', 'spark_chars', 'walk_join', 'walk', 'exttypes', 'globtastic'
26
26
  'ssh', 'rsync_multi', 'run', 'open_file', 'save_pickle', 'load_pickle', 'parse_env', 'expand_wildcards',
27
27
  'atomic_save', 'load_mod', 'import_no_init', 'save_config_file', 'read_config_file', 'find_file_parents',
28
28
  'Config', 'Unset', 'dict2obj', 'obj2dict', 'repr_dict', 'is_listy', 'mapped', 'take_lines', 'IterLen',
29
- 'ReindexCollection', 'SaveReturn', 'trim_wraps', 'save_iter', 'asave_iter', 'frontmatter',
29
+ 'ReindexCollection', 'SaveReturn', 'trim_wraps', 'save_iter', 'asave_iter', 'strloader', 'frontmatter',
30
30
  'clean_cli_output', 'unqid', 'rtoken_hex', 'friendly_name', 'n_friendly_names', 'exec_eval',
31
31
  'get_source_link', 'sparkline', 'modify_exception', 'round_multiple', 'set_num_threads', 'join_path_file',
32
32
  'autostart', 'EventTimer', 'stringfmt_names', 'PartialFormatter', 'partial_format', 'truncstr', 'fenced',
@@ -736,6 +736,16 @@ def asave_iter(g):
736
736
  return _
737
737
 
738
738
  # %% ../nbs/03_xtras.ipynb #d2757e2a
739
+ def strloader():
740
+ "`yaml.BaseLoader` extended to resolve `true`/`True`/`false`/`False`, and nothing else, to `bool`"
741
+ import yaml
742
+ if not hasattr(strloader, 'ldr'):
743
+ class StrLoader(yaml.BaseLoader): pass
744
+ StrLoader.add_constructor('tag:yaml.org,2002:bool', lambda l,n: n.value.lower()=='true')
745
+ StrLoader.add_implicit_resolver('tag:yaml.org,2002:bool', re.compile(r'^(?:[Tt]rue|[Ff]alse)$'), list('tTfF'))
746
+ strloader.ldr = StrLoader
747
+ return strloader.ldr
748
+
739
749
  def frontmatter(txt:str, strvals:bool=False)->tuple:
740
750
  "Tuple of (dict, body) from frontmatter in `txt`; missing frontmatter returns ({}, txt), malformed YAML raises"
741
751
  import yaml
@@ -744,7 +754,7 @@ def frontmatter(txt:str, strvals:bool=False)->tuple:
744
754
  if end < 0:
745
755
  if not txt.endswith(('\n---','\n...')): return {},txt
746
756
  end = len(txt) - 4
747
- res = yaml.load(txt[4:end], Loader=yaml.BaseLoader if strvals else yaml.SafeLoader)
757
+ res = yaml.load(txt[4:end], Loader=strloader() if strvals else yaml.SafeLoader)
748
758
  return (res,txt[end+5:]) if isinstance(res,dict) else ({},txt)
749
759
 
750
760
  # %% ../nbs/03_xtras.ipynb #45eb5141
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastcore
3
- Version: 2.2.6
3
+ Version: 2.2.7
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
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