nbdev 2.4.13__tar.gz → 3.0.0__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 (42) hide show
  1. {nbdev-2.4.13/nbdev.egg-info → nbdev-3.0.0}/PKG-INFO +10 -26
  2. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/__init__.py +1 -1
  3. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/_modidx.py +23 -9
  4. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/clean.py +2 -2
  5. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/cli.py +14 -14
  6. nbdev-3.0.0/nbdev/config.py +366 -0
  7. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/doclinks.py +4 -5
  8. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/export.py +3 -5
  9. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/migrate.py +96 -2
  10. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/quarto.py +3 -3
  11. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/release.py +14 -26
  12. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/serve.py +2 -2
  13. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/test.py +2 -2
  14. {nbdev-2.4.13 → nbdev-3.0.0/nbdev.egg-info}/PKG-INFO +10 -26
  15. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev.egg-info/SOURCES.txt +0 -3
  16. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev.egg-info/entry_points.txt +1 -0
  17. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev.egg-info/requires.txt +5 -3
  18. nbdev-3.0.0/pyproject.toml +82 -0
  19. nbdev-2.4.13/nbdev/config.py +0 -308
  20. nbdev-2.4.13/nbdev.egg-info/not-zip-safe +0 -1
  21. nbdev-2.4.13/pyproject.toml +0 -11
  22. nbdev-2.4.13/settings.ini +0 -75
  23. nbdev-2.4.13/setup.py +0 -61
  24. {nbdev-2.4.13 → nbdev-3.0.0}/CONTRIBUTING.md +0 -0
  25. {nbdev-2.4.13 → nbdev-3.0.0}/LICENSE +0 -0
  26. {nbdev-2.4.13 → nbdev-3.0.0}/MANIFEST.in +0 -0
  27. {nbdev-2.4.13 → nbdev-3.0.0}/README.md +0 -0
  28. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/diff.py +0 -0
  29. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/extract_attachments.py +0 -0
  30. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/frontmatter.py +0 -0
  31. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/imports.py +0 -0
  32. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/maker.py +0 -0
  33. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/merge.py +0 -0
  34. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/process.py +0 -0
  35. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/processors.py +0 -0
  36. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/qmd.py +0 -0
  37. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/serve_drv.py +0 -0
  38. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/showdoc.py +0 -0
  39. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev/sync.py +0 -0
  40. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev.egg-info/dependency_links.txt +0 -0
  41. {nbdev-2.4.13 → nbdev-3.0.0}/nbdev.egg-info/top_level.txt +0 -0
  42. {nbdev-2.4.13 → nbdev-3.0.0}/setup.cfg +0 -0
@@ -1,26 +1,22 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nbdev
3
- Version: 2.4.13
3
+ Version: 3.0.0
4
4
  Summary: Create delightful software with Jupyter Notebooks
5
- Home-page: https://github.com/AnswerDotAI/nbdev
6
- Author: Jeremy Howard and Hamel Husain
7
- Author-email: j@fast.ai
8
- License: Apache Software License 2.0
5
+ Author-email: "Jeremy Howard and the fast.ai community" <j@fast.ai>
6
+ License: Apache-2.0
7
+ Project-URL: Repository, https://github.com/AnswerDotAI/nbdev
9
8
  Project-URL: Documentation, https://nbdev.fast.ai/
10
- Keywords: nbdev fastai jupyter notebook export
9
+ Keywords: nbdev,fastai,jupyter,notebook,export
11
10
  Classifier: Development Status :: 5 - Production/Stable
12
11
  Classifier: Intended Audience :: Developers
13
12
  Classifier: Natural Language :: English
14
- Classifier: Programming Language :: Python :: 3.9
15
- Classifier: Programming Language :: Python :: 3.10
16
- Classifier: Programming Language :: Python :: 3.12
17
- Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
18
15
  Classifier: License :: OSI Approved :: Apache Software License
19
16
  Requires-Python: >=3.9
20
17
  Description-Content-Type: text/markdown
21
18
  License-File: LICENSE
22
- Requires-Dist: packaging
23
- Requires-Dist: fastcore>=1.12.1
19
+ Requires-Dist: fastcore>=1.12.3
24
20
  Requires-Dist: execnb>=0.1.12
25
21
  Requires-Dist: astunparse
26
22
  Requires-Dist: ghapi>=1.0.3
@@ -29,7 +25,8 @@ Requires-Dist: asttokens
29
25
  Requires-Dist: setuptools
30
26
  Requires-Dist: build
31
27
  Requires-Dist: fastgit
32
- Requires-Dist: PyYAML
28
+ Requires-Dist: pyyaml
29
+ Requires-Dist: tomli; python_version < "3.11"
33
30
  Provides-Extra: dev
34
31
  Requires-Dist: ipywidgets; extra == "dev"
35
32
  Requires-Dist: nbdev-numpy; extra == "dev"
@@ -43,20 +40,7 @@ Requires-Dist: pysymbol_llm; extra == "dev"
43
40
  Requires-Dist: llms-txt; extra == "dev"
44
41
  Requires-Dist: sphinx; extra == "dev"
45
42
  Requires-Dist: plum-dispatch; extra == "dev"
46
- Dynamic: author
47
- Dynamic: author-email
48
- Dynamic: classifier
49
- Dynamic: description
50
- Dynamic: description-content-type
51
- Dynamic: home-page
52
- Dynamic: keywords
53
- Dynamic: license
54
43
  Dynamic: license-file
55
- Dynamic: project-url
56
- Dynamic: provides-extra
57
- Dynamic: requires-dist
58
- Dynamic: requires-python
59
- Dynamic: summary
60
44
 
61
45
  # Getting Started
62
46
 
@@ -1,4 +1,4 @@
1
- __version__ = "2.4.13"
1
+ __version__ = "3.0.0"
2
2
 
3
3
  from .doclinks import nbdev_export
4
4
  from .showdoc import show_doc
@@ -28,23 +28,31 @@ d = { 'settings': { 'branch': 'main',
28
28
  'nbdev.cli.nbdev_new': ('api/cli.html#nbdev_new', 'nbdev/cli.py'),
29
29
  'nbdev.cli.nbdev_update_license': ('api/cli.html#nbdev_update_license', 'nbdev/cli.py'),
30
30
  'nbdev.cli.watch_export': ('api/cli.html#watch_export', 'nbdev/cli.py')},
31
- 'nbdev.config': { 'nbdev.config._apply_defaults': ('api/config.html#_apply_defaults', 'nbdev/config.py'),
31
+ 'nbdev.config': { 'nbdev.config.ConfigToml': ('api/config.html#configtoml', 'nbdev/config.py'),
32
+ 'nbdev.config.ConfigToml.__getattr__': ('api/config.html#configtoml.__getattr__', 'nbdev/config.py'),
33
+ 'nbdev.config.ConfigToml.__getitem__': ('api/config.html#configtoml.__getitem__', 'nbdev/config.py'),
34
+ 'nbdev.config.ConfigToml.__init__': ('api/config.html#configtoml.__init__', 'nbdev/config.py'),
35
+ 'nbdev.config.ConfigToml.d': ('api/config.html#configtoml.d', 'nbdev/config.py'),
36
+ 'nbdev.config.ConfigToml.get': ('api/config.html#configtoml.get', 'nbdev/config.py'),
37
+ 'nbdev.config.ConfigToml.path': ('api/config.html#configtoml.path', 'nbdev/config.py'),
38
+ 'nbdev.config.ConfigToml.version': ('api/config.html#configtoml.version', 'nbdev/config.py'),
32
39
  'nbdev.config._basic_export_nb': ('api/config.html#_basic_export_nb', 'nbdev/config.py'),
33
- 'nbdev.config._cfg2txt': ('api/config.html#_cfg2txt', 'nbdev/config.py'),
34
40
  'nbdev.config._fetch_from_git': ('api/config.html#_fetch_from_git', 'nbdev/config.py'),
41
+ 'nbdev.config._find_nbdev_pyproject': ('api/config.html#_find_nbdev_pyproject', 'nbdev/config.py'),
35
42
  'nbdev.config._get_info': ('api/config.html#_get_info', 'nbdev/config.py'),
36
43
  'nbdev.config._git_repo': ('api/config.html#_git_repo', 'nbdev/config.py'),
44
+ 'nbdev.config._has_nbdev': ('api/config.html#_has_nbdev', 'nbdev/config.py'),
37
45
  'nbdev.config._has_py': ('api/config.html#_has_py', 'nbdev/config.py'),
38
- 'nbdev.config._nbdev_config_file': ('api/config.html#_nbdev_config_file', 'nbdev/config.py'),
39
- 'nbdev.config._prompt_user': ('api/config.html#_prompt_user', 'nbdev/config.py'),
40
- 'nbdev.config._type': ('api/config.html#_type', 'nbdev/config.py'),
41
- 'nbdev.config._xdg_config_paths': ('api/config.html#_xdg_config_paths', 'nbdev/config.py'),
46
+ 'nbdev.config._load_toml': ('api/config.html#_load_toml', 'nbdev/config.py'),
47
+ 'nbdev.config._user_config': ('api/config.html#_user_config', 'nbdev/config.py'),
42
48
  'nbdev.config.add_init': ('api/config.html#add_init', 'nbdev/config.py'),
43
- 'nbdev.config.config_key': ('api/config.html#config_key', 'nbdev/config.py'),
49
+ 'nbdev.config.bump_version': ('api/config.html#bump_version', 'nbdev/config.py'),
44
50
  'nbdev.config.create_output': ('api/config.html#create_output', 'nbdev/config.py'),
45
51
  'nbdev.config.get_config': ('api/config.html#get_config', 'nbdev/config.py'),
46
52
  'nbdev.config.is_nbdev': ('api/config.html#is_nbdev', 'nbdev/config.py'),
47
53
  'nbdev.config.nbdev_create_config': ('api/config.html#nbdev_create_config', 'nbdev/config.py'),
54
+ 'nbdev.config.read_version': ('api/config.html#read_version', 'nbdev/config.py'),
55
+ 'nbdev.config.set_version': ('api/config.html#set_version', 'nbdev/config.py'),
48
56
  'nbdev.config.show_src': ('api/config.html#show_src', 'nbdev/config.py'),
49
57
  'nbdev.config.update_proj': ('api/config.html#update_proj', 'nbdev/config.py'),
50
58
  'nbdev.config.update_version': ('api/config.html#update_version', 'nbdev/config.py'),
@@ -145,15 +153,20 @@ d = { 'settings': { 'branch': 'main',
145
153
  'nbdev.merge.unpatch': ('api/merge.html#unpatch', 'nbdev/merge.py')},
146
154
  'nbdev.migrate': { 'nbdev.migrate.MigrateProc': ('api/migrate.html#migrateproc', 'nbdev/migrate.py'),
147
155
  'nbdev.migrate.MigrateProc.begin': ('api/migrate.html#migrateproc.begin', 'nbdev/migrate.py'),
156
+ 'nbdev.migrate._build_classifiers': ('api/migrate.html#_build_classifiers', 'nbdev/migrate.py'),
148
157
  'nbdev.migrate._cat_slug': ('api/migrate.html#_cat_slug', 'nbdev/migrate.py'),
149
158
  'nbdev.migrate._co': ('api/migrate.html#_co', 'nbdev/migrate.py'),
150
159
  'nbdev.migrate._convert_callout': ('api/migrate.html#_convert_callout', 'nbdev/migrate.py'),
151
160
  'nbdev.migrate._convert_video': ('api/migrate.html#_convert_video', 'nbdev/migrate.py'),
152
161
  'nbdev.migrate._file_slug': ('api/migrate.html#_file_slug', 'nbdev/migrate.py'),
162
+ 'nbdev.migrate._fmt_script': ('api/migrate.html#_fmt_script', 'nbdev/migrate.py'),
153
163
  'nbdev.migrate._fp_convert': ('api/migrate.html#_fp_convert', 'nbdev/migrate.py'),
154
164
  'nbdev.migrate._fp_fm': ('api/migrate.html#_fp_fm', 'nbdev/migrate.py'),
155
165
  'nbdev.migrate._fp_image': ('api/migrate.html#_fp_image', 'nbdev/migrate.py'),
156
166
  'nbdev.migrate._is_jekyll_post': ('api/migrate.html#_is_jekyll_post', 'nbdev/migrate.py'),
167
+ 'nbdev.migrate._migrate_workflows': ('api/migrate.html#_migrate_workflows', 'nbdev/migrate.py'),
168
+ 'nbdev.migrate._nbdev_migrate_config': ('api/migrate.html#_nbdev_migrate_config', 'nbdev/migrate.py'),
169
+ 'nbdev.migrate._py_val': ('api/migrate.html#_py_val', 'nbdev/migrate.py'),
157
170
  'nbdev.migrate._re_v1': ('api/migrate.html#_re_v1', 'nbdev/migrate.py'),
158
171
  'nbdev.migrate._repl_directives': ('api/migrate.html#_repl_directives', 'nbdev/migrate.py'),
159
172
  'nbdev.migrate._repl_v1dir': ('api/migrate.html#_repl_v1dir', 'nbdev/migrate.py'),
@@ -161,11 +174,13 @@ d = { 'settings': { 'branch': 'main',
161
174
  'nbdev.migrate._replace_fm': ('api/migrate.html#_replace_fm', 'nbdev/migrate.py'),
162
175
  'nbdev.migrate._rm_quote': ('api/migrate.html#_rm_quote', 'nbdev/migrate.py'),
163
176
  'nbdev.migrate._subv1': ('api/migrate.html#_subv1', 'nbdev/migrate.py'),
177
+ 'nbdev.migrate._toml_val': ('api/migrate.html#_toml_val', 'nbdev/migrate.py'),
164
178
  'nbdev.migrate._v': ('api/migrate.html#_v', 'nbdev/migrate.py'),
165
179
  'nbdev.migrate.fp_md_fm': ('api/migrate.html#fp_md_fm', 'nbdev/migrate.py'),
166
180
  'nbdev.migrate.migrate_md': ('api/migrate.html#migrate_md', 'nbdev/migrate.py'),
167
181
  'nbdev.migrate.migrate_nb': ('api/migrate.html#migrate_nb', 'nbdev/migrate.py'),
168
- 'nbdev.migrate.nbdev_migrate': ('api/migrate.html#nbdev_migrate', 'nbdev/migrate.py')},
182
+ 'nbdev.migrate.nbdev_migrate': ('api/migrate.html#nbdev_migrate', 'nbdev/migrate.py'),
183
+ 'nbdev.migrate.nbdev_migrate_config': ('api/migrate.html#nbdev_migrate_config', 'nbdev/migrate.py')},
169
184
  'nbdev.process': { 'nbdev.process.NBProcessor': ('api/process.html#nbprocessor', 'nbdev/process.py'),
170
185
  'nbdev.process.NBProcessor.__init__': ('api/process.html#nbprocessor.__init__', 'nbdev/process.py'),
171
186
  'nbdev.process.NBProcessor._proc': ('api/process.html#nbprocessor._proc', 'nbdev/process.py'),
@@ -303,7 +318,6 @@ d = { 'settings': { 'branch': 'main',
303
318
  'nbdev.release._run': ('api/release.html#_run', 'nbdev/release.py'),
304
319
  'nbdev.release._write_yaml': ('api/release.html#_write_yaml', 'nbdev/release.py'),
305
320
  'nbdev.release.anaconda_upload': ('api/release.html#anaconda_upload', 'nbdev/release.py'),
306
- 'nbdev.release.bump_version': ('api/release.html#bump_version', 'nbdev/release.py'),
307
321
  'nbdev.release.changelog': ('api/release.html#changelog', 'nbdev/release.py'),
308
322
  'nbdev.release.chk_conda_rel': ('api/release.html#chk_conda_rel', 'nbdev/release.py'),
309
323
  'nbdev.release.conda_output_path': ('api/release.html#conda_output_path', 'nbdev/release.py'),
@@ -125,8 +125,8 @@ def process_write(warn_msg, proc_nb, f_in, f_out=None, disp=False):
125
125
  def _nbdev_clean(nb, path=None, clear_all=None):
126
126
  cfg = get_config(path=path)
127
127
  clear_all = clear_all or cfg.clear_all
128
- allowed_metadata_keys = cfg.get("allowed_metadata_keys").split()
129
- allowed_cell_metadata_keys = cfg.get("allowed_cell_metadata_keys").split()
128
+ allowed_metadata_keys = cfg.get("allowed_metadata_keys") or []
129
+ allowed_cell_metadata_keys = cfg.get("allowed_cell_metadata_keys") or []
130
130
  clean_nb(nb, clear_all, allowed_metadata_keys, allowed_cell_metadata_keys, cfg.clean_ids)
131
131
  if path: nbdev_trust.__wrapped__(path)
132
132
 
@@ -65,7 +65,7 @@ def _render_nb(fn, cfg):
65
65
  "Render templated values like `{{lib_name}}` in notebook at `fn` from `cfg`"
66
66
  txt = fn.read_text()
67
67
  txt = txt.replace('from your_lib.core', f'from {cfg.lib_path}.core') # for compatibility with old templates
68
- for k,v in cfg.d.items(): txt = txt.replace('{{'+k+'}}', v)
68
+ for k,v in cfg.items(): txt = txt.replace('{{'+k+'}}', str(v))
69
69
  fn.write_text(txt)
70
70
 
71
71
  # %% ../nbs/api/13_cli.ipynb #dd385911-aa8f-44e7-8d46-7b8a20f3b010
@@ -87,10 +87,11 @@ def nbdev_new(**kwargs):
87
87
  from ghapi.core import GhApi
88
88
  nbdev_create_config.__wrapped__(**kwargs)
89
89
  cfg = get_config()
90
- _update_repo_meta(cfg)
90
+ if (Path('.git')).exists(): _update_repo_meta(cfg)
91
+ else: print(f"No git repo found. Run: gh repo create {cfg.user}/{cfg.repo} --public --source=.")
91
92
  path = Path()
92
93
 
93
- _ORG_OR_USR,_REPOSITORY = 'answerdotai','nbdev-template'
94
+ _ORG_OR_USR,_REPOSITORY = 'answerdotai','nbdev3-template'
94
95
  _TEMPLATE = f'{_ORG_OR_USR}/{_REPOSITORY}'
95
96
  template = kwargs.get('template', _TEMPLATE)
96
97
  try: org_or_usr, repo = template.split('/')
@@ -143,24 +144,23 @@ def nbdev_update_license(
143
144
  avail_lic = GhApi().licenses.get_all_commonly_used().map(lambda x: x['key'])
144
145
 
145
146
  cfg = get_config()
146
- curr_lic = cfg['license']
147
+ curr_lic = cfg.license
147
148
 
148
149
  mapped = mapping.get(to, None)
149
-
150
150
  if mapped not in avail_lic: raise ValueError(f"{to} is not an available license")
151
151
  body = GhApi().licenses.get(mapped)['body']
152
152
 
153
- body = body.replace('[year], [fullname]', cfg['copyright'])
154
- body = body.replace('[year] [fullname]', cfg['copyright'])
155
-
156
- content = open("settings.ini", "r").read()
157
- content = re.sub(r"^(license\s*=\s*).*?$", r"\1 " + to, content, flags=re.MULTILINE)
153
+ copyright = f"{datetime.now().year}, {cfg.author}"
154
+ body = body.replace('[year], [fullname]', copyright)
155
+ body = body.replace('[year] [fullname]', copyright)
158
156
 
159
- config = open("settings.ini", "w")
160
- config.write(content)
157
+ # Update pyproject.toml
158
+ pyproj = cfg.config_file
159
+ content = pyproj.read_text()
160
+ content = re.sub(r'^(license\s*=\s*\{text\s*=\s*").*?(")', rf'\g<1>{to}\2', content, flags=re.MULTILINE)
161
+ pyproj.write_text(content)
161
162
 
162
- lic = open('LICENSE', 'w')
163
- lic.write(body)
163
+ Path('LICENSE').write_text(body)
164
164
  print(f"License updated from {curr_lic} to {to}")
165
165
 
166
166
  # %% ../nbs/api/13_cli.ipynb #412b4cd2
@@ -0,0 +1,366 @@
1
+ """Configuring nbdev and bootstrapping notebook export"""
2
+
3
+ # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/01_config.ipynb.
4
+
5
+ # %% auto #0
6
+ __all__ = ['pyproject_nm', 'pyproject_tmpl', 'nbdev_defaults', 'pyproj_tmpl', 'nbdev_create_config', 'ConfigToml', 'get_config',
7
+ 'is_nbdev', 'create_output', 'show_src', 'read_version', 'set_version', 'bump_version', 'update_version',
8
+ 'update_proj', 'add_init', 'write_cells']
9
+
10
+ # %% ../nbs/api/01_config.ipynb #6fd14ecd
11
+ from datetime import datetime
12
+ from fastcore.docments import *
13
+ from fastcore.utils import *
14
+ from fastcore.meta import *
15
+ from fastcore.script import *
16
+ from fastcore.style import *
17
+ from fastcore.xdg import *
18
+
19
+ import ast,warnings,tomli
20
+ from IPython.display import Markdown
21
+ from execnb.nbio import read_nb,NbCell
22
+ from urllib.error import HTTPError
23
+
24
+ # %% ../nbs/api/01_config.ipynb #117128e6
25
+ pyproject_nm = 'pyproject.toml'
26
+ _nbdev_home_dir = 'nbdev'
27
+ _user_cfg_name = 'config.toml'
28
+
29
+ # %% ../nbs/api/01_config.ipynb #adf0834f
30
+ def _git_repo():
31
+ try: return repo_details(run('git config --get remote.origin.url'))[1]
32
+ except OSError: return
33
+
34
+ # %% ../nbs/api/01_config.ipynb #6eeafafd
35
+ def _get_info(owner, repo, default_branch='main', default_kw='nbdev'):
36
+ from ghapi.all import GhApi
37
+ api = GhApi(owner=owner, repo=repo, token=os.getenv('GITHUB_TOKEN'))
38
+
39
+ try: r = api.repos.get()
40
+ except HTTPError:
41
+ msg= [f"""Could not access repo: {owner}/{repo} to find your default branch - `{default_branch}` assumed.
42
+ Edit `pyproject.toml` if this is incorrect.
43
+ In the future, you can allow nbdev to see private repos by setting the environment variable GITHUB_TOKEN as described here:
44
+ https://nbdev.fast.ai/api/release.html#setup"""]
45
+ print(''.join(msg))
46
+ return default_branch,default_kw,''
47
+
48
+ return r.default_branch, default_kw if not getattr(r, 'topics', []) else ' '.join(r.topics), r.description
49
+
50
+ # %% ../nbs/api/01_config.ipynb #35d5c037
51
+ def _fetch_from_git(raise_err=False):
52
+ "Get information for pyproject.toml from git."
53
+ res={}
54
+ try:
55
+ url = run('git config --get remote.origin.url')
56
+ res['user'],res['repo'] = repo_details(url)
57
+ res['branch'],res['keywords'],desc = _get_info(owner=res['user'], repo=res['repo'])
58
+ if desc: res['description'] = desc
59
+ res['author'] = run('git config --get user.name').strip()
60
+ res['author_email'] = run('git config --get user.email').strip()
61
+ except OSError as e:
62
+ if raise_err: raise(e)
63
+ else: res['lib_name'] = res['repo'].replace('-','_')
64
+ return res
65
+
66
+ # %% ../nbs/api/01_config.ipynb #05aae09f
67
+ pyproject_tmpl = '''[build-system]
68
+ requires = ["setuptools>=64"]
69
+ build-backend = "setuptools.build_meta"
70
+
71
+ [project]
72
+ name = "{name}"
73
+ dynamic = ["version"]
74
+ description = "{description}"
75
+ readme = "README.md"
76
+ requires-python = ">={min_python}"
77
+ license = {{text = "{license}"}}
78
+ authors = [{{name = "{author}", email = "{author_email}"}}]
79
+ keywords = {keywords}
80
+ classifiers = [
81
+ "Programming Language :: Python :: 3",
82
+ "Programming Language :: Python :: 3 :: Only",
83
+ ]
84
+ dependencies = []
85
+
86
+ [project.urls]
87
+ Repository = "{git_url}"
88
+ Documentation = "{doc_url}"
89
+
90
+ [project.entry-points.nbdev]
91
+ {lib_path} = "{lib_path}._modidx:d"
92
+
93
+ [tool.setuptools.dynamic]
94
+ version = {{attr = "{lib_path}.__version__"}}
95
+
96
+ [tool.setuptools.packages.find]
97
+ include = ["{lib_path}"]
98
+
99
+ [tool.nbdev]
100
+ '''
101
+
102
+ # %% ../nbs/api/01_config.ipynb #3fc5c33f
103
+ @call_parse
104
+ def nbdev_create_config(
105
+ repo:str=None, # Repo name
106
+ branch:str='main', # Repo default branch
107
+ user:str=None, # Repo username
108
+ author:str=None, # Package author's name
109
+ author_email:str=None, # Package author's email address
110
+ description:str='', # Short summary of the package
111
+ path:str='.', # Path to create config file
112
+ min_python:str='3.10', # Minimum Python version
113
+ license:str='Apache-2.0', # License (SPDX identifier)
114
+ ):
115
+ "Create a pyproject.toml config file."
116
+ path = Path(path)
117
+ path.mkdir(exist_ok=True, parents=True)
118
+
119
+ # Infer from git if not provided
120
+ inf = _fetch_from_git()
121
+ repo = repo or inf.get('repo') or path.resolve().name
122
+ user = user or inf.get('user', '')
123
+ if not user: raise ValueError("Could not infer `user` from git. Please pass --user explicitly.")
124
+ author = author or inf.get('author', '')
125
+ if not author: raise ValueError("Could not infer `author` from git. Please pass --author explicitly.")
126
+ author_email = author_email or inf.get('author_email', '')
127
+ if not author_email: raise ValueError("Could not infer `author_email` from git. Please pass --author_email explicitly.")
128
+ branch = branch or inf.get('branch', 'main')
129
+ description = description or inf.get('description', '')
130
+
131
+ lib_path = repo.replace('-', '_')
132
+ git_url = f"https://github.com/{user}/{repo}" if user else ''
133
+ doc_url = f"https://{user}.github.io/{repo}/" if user else ''
134
+ keywords = inf.get('keywords', 'nbdev').split()
135
+
136
+ txt = pyproject_tmpl.format(name=repo, lib_path=lib_path, description=description, min_python=min_python, license=license,
137
+ author=author, author_email=author_email, keywords=keywords, git_url=git_url, doc_url=doc_url, branch=branch)
138
+
139
+ cfg_file = path / pyproject_nm
140
+ if cfg_file.exists(): warn(f'{cfg_file} already exists')
141
+ else:
142
+ cfg_file.write_text(txt)
143
+ print(f'{cfg_file} created.')
144
+
145
+ # %% ../nbs/api/01_config.ipynb #0e56064f
146
+ def _load_toml(p):
147
+ "Load TOML file at `p` into a dict"
148
+ return tomli.loads(Path(p).read_text(encoding='utf-8'))
149
+
150
+ def _has_nbdev(p):
151
+ "True if pyproject.toml at `p` has [tool.nbdev]"
152
+ try: return bool(_load_toml(p).get('tool', {}).get('nbdev', {}))
153
+ except Exception: return False
154
+
155
+ def _find_nbdev_pyproject(path=None):
156
+ "Find nearest pyproject.toml containing [tool.nbdev], walking up from `path`"
157
+ p = Path(path or Path.cwd()).resolve()
158
+ for d in [p] + list(p.parents):
159
+ f = d/pyproject_nm
160
+ if f.exists() and _has_nbdev(f): return f
161
+
162
+ # %% ../nbs/api/01_config.ipynb #3dac70e0
163
+ nbdev_defaults = dict(nbs_path='nbs', doc_path='_docs', tst_flags='notest', recursive=True, readme_nb='index.ipynb',
164
+ clean_ids=True, clear_all=False, put_version_in_init=True, jupyter_hooks=False, black_formatting=False, branch='main')
165
+
166
+ _path_keys = 'lib_path', 'nbs_path', 'doc_path'
167
+
168
+ # %% ../nbs/api/01_config.ipynb #165ff301
169
+ class ConfigToml(AttrDict):
170
+ def __init__(self, d, proj, cfg_file):
171
+ super().__init__({**nbdev_defaults, **d})
172
+ self.config_file = cfg_file
173
+ self.config_path = cfg_file.parent
174
+
175
+ self.lib_name = proj.get('name', '')
176
+ self.title = self.get('title') or self.lib_name
177
+ self.description = proj.get('description', '')
178
+ self.keywords = proj.get('keywords', [])
179
+ self.min_python = (proj.get('requires-python') or '>=3.9').lstrip('>=')
180
+ auths = proj.get('authors') or [{}]
181
+ self.author = auths[0].get('name')
182
+ self.author_email = auths[0].get('email')
183
+
184
+
185
+ urls = proj.get('urls') or {}
186
+ self.git_url = (urls.get('Repository') or urls.get('Source') or '').rstrip('/')
187
+ self.doc_url = urls.get('Documentation') or ''
188
+ self.user, self.repo = repo_details(self.git_url) if self.git_url else ('', '')
189
+ # Derive doc_host and doc_baseurl from doc_url
190
+ from urllib.parse import urlparse
191
+ u = urlparse(self.doc_url)
192
+ self.doc_host = f"{u.scheme}://{u.netloc}" if u.scheme else ''
193
+ self.doc_baseurl = (u.path or '/').rstrip('/') or '/'
194
+ if 'lib_path' not in self: self['lib_path'] = self.lib_name.replace('-', '_')
195
+
196
+ @property
197
+ def version(self):
198
+ return read_version(self.config_path / self['lib_path']) or '0.0.1'
199
+
200
+ @property
201
+ def d(self): return {k:v for k,v in super().items()}
202
+
203
+ def __getattr__(self, k): return stop(AttributeError(k)) if k=='d' or k not in self.d else self.get(k)
204
+ def __getitem__(self, k): return stop(IndexError(k)) if k not in self.d else self.get(k)
205
+
206
+ def get(self, k, default=None):
207
+ v = self.d.get(k, default)
208
+ if v is None: return None
209
+ return self.config_path / v if k in _path_keys else v
210
+
211
+ def path(self, k, default=None):
212
+ v = self.d.get(k, default)
213
+ return v if v is None else self.config_path / v
214
+
215
+ # %% ../nbs/api/01_config.ipynb #6fed9c91
216
+ def _user_config():
217
+ "Load user config from ~/.config/nbdev/config.toml if it exists"
218
+ p = xdg_config_home() / _nbdev_home_dir / _user_cfg_name
219
+ if p.exists(): return _load_toml(p)
220
+ return {}
221
+
222
+ # %% ../nbs/api/01_config.ipynb #f6660849
223
+ def get_config(path=None, also_settings=False):
224
+ "Return nbdev config."
225
+ cfg_file = _find_nbdev_pyproject(path)
226
+ if cfg_file is not None:
227
+ # Check for old settings.ini and complain loudly
228
+ old_cfg = cfg_file.parent / 'settings.ini'
229
+ if old_cfg.exists() and not also_settings:
230
+ raise ValueError(f"Found old settings.ini at {old_cfg}. Please migrate to pyproject.toml using `nbdev_migrate`. See https://nbdev.fast.ai/getting_started.html for details.")
231
+ d = _load_toml(cfg_file)
232
+ user = _user_config()
233
+ nbdev = {**user, **d.get('tool', {}).get('nbdev', {})}
234
+ return ConfigToml(nbdev, d.get('project', {}), cfg_file)
235
+ if also_settings:
236
+ from fastcore.foundation import Config
237
+ cfg = Config.find('settings.ini', path)
238
+ if cfg: return cfg
239
+ cfg_path = Path(path or Path.cwd()).expanduser().absolute()
240
+ return ConfigToml(nbdev_defaults, {}, cfg_path/'pyproject.toml')
241
+
242
+ # %% ../nbs/api/01_config.ipynb #6939b40e
243
+ def is_nbdev(path=None): return _find_nbdev_pyproject(path) is not None
244
+
245
+ # %% ../nbs/api/01_config.ipynb #6e89fe6c
246
+ def create_output(txt, mime):
247
+ "Add a cell output containing `txt` of the `mime` text MIME sub-type"
248
+ return [dict(data={f"text/{mime}": str(txt).splitlines(True)},
249
+ execution_count=1, metadata={}, output_type="execute_result")]
250
+
251
+ # %% ../nbs/api/01_config.ipynb #5a4d8e52
252
+ def show_src(src, lang='python'): return Markdown(f'```{lang}\n{src}\n```')
253
+
254
+ # %% ../nbs/api/01_config.ipynb #163177f2
255
+ pyproj_tmpl = """[build-system]
256
+ requires = ["setuptools>=64.0"]
257
+ build-backend = "setuptools.build_meta"
258
+
259
+ [project]
260
+ name = "FILL_IN"
261
+ requires-python="FILL_IN"
262
+ dynamic = [ "keywords", "description", "version", "dependencies", "optional-dependencies", "readme",
263
+ "license", "authors", "classifiers", "entry-points", "scripts", "urls"]
264
+
265
+ [tool.uv]
266
+ cache-keys = [{ file = "pyproject.toml" }, { file = "setup.py" }]
267
+ """
268
+
269
+ # %% ../nbs/api/01_config.ipynb #f1c85f45
270
+ _re_version = re.compile(r'^__version__\s*=\s*[\'"]([^\'"]+)[\'"]', re.MULTILINE)
271
+ _re_proj = re.compile(r'^name\s*=\s*".*$', re.MULTILINE)
272
+ _re_reqpy = re.compile(r'^requires-python\s*=\s*".*$', re.MULTILINE)
273
+ _init = '__init__.py'
274
+ _pyproj = 'pyproject.toml'
275
+
276
+ # %% ../nbs/api/01_config.ipynb #680e62a5
277
+ def read_version(path):
278
+ "Read __version__ from `path/__init__.py`, or None if not found"
279
+ fname = Path(path)/_init
280
+ if not fname.exists(): return None
281
+ m = _re_version.search(fname.read_text())
282
+ return m.group(1) if m else None
283
+
284
+ # %% ../nbs/api/01_config.ipynb #cf0fc6a3
285
+ def set_version(path, version):
286
+ "Set __version__ in `path/__init__.py`"
287
+ path = Path(path)
288
+ path.mkdir(exist_ok=True, parents=True)
289
+ fname = path/_init
290
+ if not fname.exists(): fname.touch()
291
+ ver_line = f'__version__ = "{version}"'
292
+ code = fname.read_text()
293
+ if _re_version.search(code) is None: code = ver_line + "\n" + code
294
+ else: code = _re_version.sub(ver_line, code)
295
+ fname.write_text(code)
296
+
297
+ # %% ../nbs/api/01_config.ipynb #d00889e5
298
+ def bump_version(v, part=2, unbump=False):
299
+ "Bump semver string `v` at index `part` (0=major, 1=minor, 2=patch)"
300
+ parts = (v or '0.0.0').split('.')
301
+ parts += ['0'] * (3 - len(parts))
302
+ parts[part] = str(int(parts[part]) + (-1 if unbump else 1))
303
+ for i in range(part+1, 3): parts[i] = '0'
304
+ return '.'.join(parts[:3])
305
+
306
+ # %% ../nbs/api/01_config.ipynb #e32583e6
307
+ def update_version(path=None):
308
+ "Add __version__ to `path/__init__.py` if it doesn't exist"
309
+ path = Path(path or get_config().lib_path)
310
+ if read_version(path) is None: set_version(path, get_config().version)
311
+
312
+ # %% ../nbs/api/01_config.ipynb #275ef862
313
+ def _has_py(fs): return any(1 for f in fs if f.endswith('.py'))
314
+
315
+ def update_proj(path):
316
+ "Create or update `pyproject.toml` in the project root."
317
+ fname = path/_pyproj
318
+ if not fname.exists(): fname.write_text(pyproj_tmpl)
319
+ txt = fname.read_text()
320
+ txt = _re_proj.sub(f'name = "{get_config().lib_name}"', txt)
321
+ txt = _re_reqpy.sub(f'requires-python = ">={get_config().min_python}"', txt)
322
+ fname.write_text(txt)
323
+
324
+ # %% ../nbs/api/01_config.ipynb #bdf57184
325
+ def add_init(path=None):
326
+ "Add `__init__.py` in all subdirs of `path` containing python files if it's not there already."
327
+ # we add the lowest-level `__init__.py` files first, which ensures _has_py succeeds for parent modules
328
+ path = Path(path or get_config().lib_path)
329
+ path.mkdir(exist_ok=True)
330
+ if not (path/_init).exists(): (path/_init).touch()
331
+ for r,ds,fs in os.walk(path, topdown=False):
332
+ r = Path(r)
333
+ subds = (os.listdir(r/d) for d in ds)
334
+ if _has_py(fs) or any(filter(_has_py, subds)) and not (r/_init).exists(): (r/_init).touch()
335
+ if get_config().get('put_version_in_init', True): update_version(path)
336
+ if get_config().get('update_pyproject', True): update_proj(path.parent)
337
+
338
+ # %% ../nbs/api/01_config.ipynb #cdd05b4c
339
+ def write_cells(cells, hdr, file, solo_nb=False):
340
+ "Write `cells` to `file` along with header `hdr` (mainly for nbdev internal use)."
341
+ for cell in cells:
342
+ if cell.cell_type=='code' and cell.source.strip():
343
+ cell_id = f" #{cell.id}" if cell.get('id') else ""
344
+ file.write(f'\n\n{hdr}{cell_id}\n{cell.source}') if not solo_nb else file.write(f'\n\n{cell.source}')
345
+
346
+ # %% ../nbs/api/01_config.ipynb #34cfeb82
347
+ def _basic_export_nb(fname, name, dest=None):
348
+ "Basic exporter to bootstrap nbdev."
349
+ if dest is None: dest = get_config().lib_path
350
+ add_init()
351
+ fname,dest = Path(fname),Path(dest)
352
+ nb = read_nb(fname)
353
+
354
+ # grab the source from all the cells that have an `export` comment
355
+ cells = L(cell for cell in nb.cells if re.match(r'#\s*\|export', cell.source))
356
+
357
+ # find all the exported functions, to create `__all__`:
358
+ trees = cells.map(NbCell.parsed_).concat()
359
+ funcs = trees.filter(risinstance((ast.FunctionDef,ast.ClassDef))).attrgot('name')
360
+ exp_funcs = [f for f in funcs if f[0]!='_']
361
+
362
+ # write out the file
363
+ with (dest/name).open('w',encoding="utf-8") as f:
364
+ f.write(f"# %% auto 0\n__all__ = {exp_funcs}")
365
+ write_cells(cells, f"# %% {fname.relpath(dest)}", f)
366
+ f.write('\n')
@@ -107,8 +107,7 @@ def _build_modidx(dest=None, nbs_path=None, skip_exists=False):
107
107
  with contextlib.suppress(FileNotFoundError): idxfile.unlink()
108
108
  if idxfile.exists(): res = exec_local(idxfile.read_text(encoding='utf-8'), 'd')
109
109
  else: res = dict(syms={}, settings={})
110
- res['settings'] = {k:v for k,v in get_config().d.items()
111
- if k in ('doc_host','doc_baseurl','lib_path','git_url','branch')}
110
+ res['settings'] = {k:v for k,v in get_config().d.items() if k in ('doc_host','doc_baseurl','lib_path','git_url','branch')}
112
111
  code_root = dest.parent.resolve()
113
112
  for file in globtastic(dest, file_glob="*.py", skip_file_re='^_', skip_folder_re=r"\.ipynb_checkpoints"):
114
113
  try: res['syms'].update(_get_modidx((dest.parent/file).resolve(), code_root, nbs_path=nbs_path))
@@ -229,12 +228,12 @@ _re_backticks = re.compile(r'`([^`\s]+?)(?:\(\))?`')
229
228
  # %% ../nbs/api/05_doclinks.ipynb #3a24b883
230
229
  @lru_cache(None)
231
230
  def _build_lookup_table(strip_libs=None, incl_libs=None, skip_mods=None):
232
- cfg = get_config()
231
+ cfg = get_config(also_settings=True)
233
232
  if strip_libs is None:
234
- try: strip_libs = cfg.get('strip_libs', cfg.get('lib_path', 'nbdev').name).split()
233
+ try: strip_libs = cfg.get('strip_libs') or cfg.lib_name
235
234
  except FileNotFoundError: strip_libs = 'nbdev'
236
235
  skip_mods = setify(skip_mods)
237
- strip_libs = L(strip_libs)
236
+ strip_libs = L(strip_libs.split() if isinstance(strip_libs, str) else strip_libs)
238
237
  if incl_libs is not None: incl_libs = (L(incl_libs)+strip_libs).unique()
239
238
  entries = {}
240
239
  try: eps = entry_points(group='nbdev')
@@ -35,10 +35,9 @@ class ExportModuleProc:
35
35
 
36
36
  # %% ../nbs/api/04_export.ipynb #6f524839
37
37
  def black_format(cell, # Cell to format
38
- force=False): # Turn black formatting on regardless of settings.ini
38
+ force=False): # Turn black formatting on regardless of pyproject.toml
39
39
  "Processor to format code with `black`"
40
- try: cfg = get_config()
41
- except FileNotFoundError: return
40
+ cfg = get_config()
42
41
  if (not cfg.black_formatting and not force) or cell.cell_type != 'code': return
43
42
  try: import black
44
43
  except: raise ImportError("You must install black: `pip install black` if you wish to use black formatting with nbdev")
@@ -53,8 +52,7 @@ _magics_pattern = re.compile(r'^\s*(%%|%).*\n?', re.MULTILINE)
53
52
 
54
53
  def scrub_magics(cell): # Cell to format
55
54
  "Processor to remove cell magics from exported code"
56
- try: cfg = get_config()
57
- except FileNotFoundError: return
55
+ cfg = get_config()
58
56
  if cell.cell_type != 'code': return
59
57
  try: cell.source = _magics_pattern.sub('', cell.source)
60
58
  except: pass