omdev 0.0.0.dev43__py3-none-any.whl → 0.0.0.dev45__py3-none-any.whl

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.

Potentially problematic release.


This version of omdev might be problematic. Click here for more details.

omdev/.manifests.json CHANGED
@@ -36,14 +36,14 @@
36
36
  }
37
37
  },
38
38
  {
39
- "module": ".cli.clicmds",
39
+ "module": ".cli.clicli",
40
40
  "attr": "_CLI_MODULE",
41
- "file": "omdev/cli/clicmds.py",
42
- "line": 57,
41
+ "file": "omdev/cli/clicli.py",
42
+ "line": 62,
43
43
  "value": {
44
44
  "$.cli.types.CliModule": {
45
45
  "cmd_name": "cli",
46
- "mod_name": "omdev.cli.clicmds"
46
+ "mod_name": "omdev.cli.clicli"
47
47
  }
48
48
  }
49
49
  },
@@ -135,7 +135,7 @@
135
135
  "module": ".tools.gittools",
136
136
  "attr": "_CLI_MODULE",
137
137
  "file": "omdev/tools/gittools.py",
138
- "line": 22,
138
+ "line": 64,
139
139
  "value": {
140
140
  "$.cli.types.CliModule": {
141
141
  "cmd_name": "git",
omdev/cli/__main__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  if __name__ == '__main__':
2
- from .main import _main
2
+ from .main import _main # noqa
3
3
 
4
4
  _main()
omdev/cli/clicli.py ADDED
@@ -0,0 +1,71 @@
1
+ import inspect
2
+ import os
3
+ import subprocess
4
+ import sys
5
+
6
+ from omlish import __about__
7
+ from omlish import argparse as ap
8
+
9
+ from . import install
10
+ from .types import CliModule
11
+
12
+
13
+ class CliCli(ap.Cli):
14
+
15
+ @ap.command(name='version')
16
+ def print_version(self) -> None:
17
+ print(__about__.__version__)
18
+
19
+ @ap.command(name='revision')
20
+ def print_revision(self) -> None:
21
+ print(__about__.__revision__)
22
+
23
+ @ap.command(
24
+ ap.arg('extra_deps', nargs='*'),
25
+ )
26
+ def reinstall(self) -> None:
27
+ mod_name = globals()['__spec__'].name
28
+ tool_name = '.'.join([mod_name.partition('.')[0], 'tools', 'piptools'])
29
+
30
+ out = subprocess.check_output([
31
+ sys.executable,
32
+ '-m',
33
+ tool_name,
34
+ 'list-root-dists',
35
+ ]).decode()
36
+
37
+ deps = sorted(
38
+ ({s for l in out.splitlines() if (s := l.strip())} | set(self.args.extra_deps or []))
39
+ - {install.DEFAULT_CLI_PKG} # noqa
40
+ )
41
+
42
+ if deps:
43
+ print('Reinstalling with following additional dependencies:')
44
+ print('\n'.join(' ' + d for d in deps))
45
+ else:
46
+ print('No additional dependencies detected.')
47
+ print()
48
+ print('Continue with reinstall? (ctrl-c to cancel)')
49
+ input()
50
+
51
+ install_src = inspect.getsource(install)
52
+
53
+ os.execl(
54
+ sys.executable,
55
+ sys.executable,
56
+ '-c',
57
+ install_src,
58
+ *deps,
59
+ )
60
+
61
+
62
+ # @omlish-manifest
63
+ _CLI_MODULE = CliModule('cli', __name__)
64
+
65
+
66
+ def _main() -> None:
67
+ CliCli()()
68
+
69
+
70
+ if __name__ == '__main__':
71
+ _main()
omdev/cli/install.py CHANGED
@@ -32,7 +32,7 @@ class InstallOpts:
32
32
  extras: ta.Sequence[str] = dc.field(default_factory=list)
33
33
 
34
34
 
35
- class InstallMgr(abc.ABC):
35
+ class InstallManager(abc.ABC):
36
36
  @abc.abstractmethod
37
37
  def is_available(self) -> bool:
38
38
  raise NotImplementedError
@@ -46,7 +46,7 @@ class InstallMgr(abc.ABC):
46
46
  raise NotImplementedError
47
47
 
48
48
 
49
- class UvxInstallMgr(InstallMgr):
49
+ class UvxInstallManager(InstallManager):
50
50
  def is_available(self) -> bool:
51
51
  return bool(shutil.which('uv'))
52
52
 
@@ -80,15 +80,25 @@ class UvxInstallMgr(InstallMgr):
80
80
  *itertools.chain.from_iterable(['--with', e] for e in (opts.extras or [])),
81
81
  ])
82
82
 
83
+ subprocess.check_call([
84
+ 'uv', 'tool',
85
+ 'run',
86
+ '--from', opts.cli_pkg,
87
+ 'om',
88
+ '_post_install',
89
+ ])
83
90
 
84
- class PipxInstallMgr(InstallMgr):
91
+
92
+ class PipxInstallManager(InstallManager):
85
93
  def is_available(self) -> bool:
86
94
  return bool(shutil.which('pipx'))
87
95
 
88
- def uninstall(self, cli_pkg: str) -> None:
96
+ def _list_installed(self) -> ta.Any:
89
97
  out = subprocess.check_output(['pipx', 'list', '--json']).decode()
98
+ return json.loads(out)
90
99
 
91
- dct = json.loads(out)
100
+ def uninstall(self, cli_pkg: str) -> None:
101
+ dct = self._list_installed()
92
102
 
93
103
  if cli_pkg not in dct.get('venvs', {}):
94
104
  return
@@ -108,10 +118,19 @@ class PipxInstallMgr(InstallMgr):
108
118
  *itertools.chain.from_iterable(['--preinstall', e] for e in (opts.extras or [])),
109
119
  ])
110
120
 
121
+ dct = self._list_installed()
111
122
 
112
- INSTALL_MGRS = {
113
- 'uvx': UvxInstallMgr(),
114
- 'pipx': PipxInstallMgr(),
123
+ exe = dct['venvs'][opts.cli_pkg]['metadata']['main_package']['app_paths'][0]['__Path__']
124
+
125
+ subprocess.check_call([
126
+ exe,
127
+ '_post_install',
128
+ ])
129
+
130
+
131
+ INSTALL_MANAGERS = {
132
+ 'uvx': UvxInstallManager(),
133
+ 'pipx': PipxInstallManager(),
115
134
  }
116
135
 
117
136
 
@@ -129,22 +148,24 @@ def _main() -> None:
129
148
  if not (cli := args.cli):
130
149
  raise ValueError(f'Must specify cli')
131
150
 
151
+ cli = cli.lower().replace('_', '-')
152
+
132
153
  if not (py := args.py):
133
154
  raise ValueError(f'Must specify py')
134
155
 
135
156
  if mgr := args.mgr:
136
- if (im := INSTALL_MGRS.get(mgr)) is None:
157
+ if (im := INSTALL_MANAGERS.get(mgr)) is None:
137
158
  raise ValueError(f'Unsupported mgr: {mgr}')
138
159
  if not im.is_available():
139
160
  raise ValueError(f'Unavailable mgr: {mgr}')
140
161
  else:
141
- for im in INSTALL_MGRS.values():
162
+ for im in INSTALL_MANAGERS.values():
142
163
  if im.is_available():
143
164
  break
144
165
  else:
145
166
  raise RuntimeError("Can't find install manager")
146
167
 
147
- for m in INSTALL_MGRS.values():
168
+ for m in INSTALL_MANAGERS.values():
148
169
  if m.is_available():
149
170
  m.uninstall(cli)
150
171
 
omdev/cli/main.py CHANGED
@@ -23,7 +23,18 @@ from .types import CliModule
23
23
  ##
24
24
 
25
25
 
26
- _CLI_FUNCS: ta.Sequence[CliFunc] = []
26
+ def _post_install() -> None:
27
+ from .managers import setup_install_manager
28
+
29
+ setup_install_manager()
30
+
31
+
32
+ ##
33
+
34
+
35
+ _CLI_FUNCS: ta.Sequence[CliFunc] = [
36
+ CliFunc('_post_install', _post_install),
37
+ ]
27
38
 
28
39
 
29
40
  ##
omdev/cli/managers.py ADDED
@@ -0,0 +1,107 @@
1
+ import enum
2
+ import os.path
3
+ import site
4
+ import sys
5
+
6
+
7
+ ##
8
+
9
+
10
+ def _normalize_pkg_name(s: str) -> str:
11
+ return s.lower().replace('_', '-')
12
+
13
+
14
+ CLI_PKG = _normalize_pkg_name(__name__.split('.')[0])
15
+
16
+
17
+ ##
18
+
19
+
20
+ class ManagerType(enum.Enum):
21
+ UVX = 'uvx'
22
+ PIPX = 'pipx'
23
+
24
+
25
+ def _detect_install_manager() -> ManagerType | None:
26
+ if os.path.isfile(fp := os.path.join(sys.prefix, 'uv-receipt.toml')):
27
+ import tomllib
28
+
29
+ with open(fp) as f:
30
+ dct = tomllib.loads(f.read())
31
+
32
+ reqs = dct.get('tool', {}).get('requirements')
33
+ main_pkg = _normalize_pkg_name(reqs[0].get('name', ''))
34
+ if reqs and main_pkg == CLI_PKG:
35
+ return ManagerType.UVX
36
+
37
+ if os.path.isfile(fp := os.path.join(sys.prefix, 'pipx_metadata.json')):
38
+ import json
39
+
40
+ with open(fp) as f:
41
+ dct = json.loads(f.read())
42
+
43
+ main_pkg = _normalize_pkg_name(dct.get('main_package', {}).get('package_or_url', ''))
44
+ if main_pkg == CLI_PKG:
45
+ return ManagerType.PIPX
46
+
47
+ return None
48
+
49
+
50
+ def detect_install_manager() -> ManagerType | None:
51
+ try:
52
+ return globals()['_DETECTED_MANAGER_TYPE']
53
+ except KeyError:
54
+ pass
55
+ ret = globals()['_DETECTED_MANAGER_TYPE'] = _detect_install_manager()
56
+ return ret
57
+
58
+
59
+ ##
60
+ # Python is insistent in prepending sys.path with an empty string (translating to the current working directory),
61
+ # which leads to problems when using the cli in directories containing python packages (such as within this very
62
+ # source tree). This can't be done in cli main as the code frequently spawns other sys.executable processes which
63
+ # wouldn't know to do that, so a .pth file hack is used. Cleaning sys.path solely there is also insufficient as that
64
+ # code runs before the problematic empty string is added, so a sys.meta_path hook is prepended.
65
+ #
66
+ # See:
67
+ # https://github.com/python/cpython/blob/da1e5526aee674bb33c17a498aa3781587b9850c/Python/sysmodule.c#L3939
68
+
69
+
70
+ def _remove_empty_from_sys_path() -> None:
71
+ while '' in sys.path:
72
+ sys.path.remove('')
73
+
74
+
75
+ class _PathHackMetaFinder:
76
+ def find_spec(self, fullname, path, target=None):
77
+ _remove_empty_from_sys_path()
78
+ return None # noqa
79
+
80
+
81
+ def _activate_path_hack() -> None:
82
+ if not any(isinstance(mp, _PathHackMetaFinder) for mp in sys.meta_path):
83
+ sys.meta_path.insert(0, _PathHackMetaFinder())
84
+
85
+
86
+ _PATH_HACK_FILE_NAME = f'{"-".join(__name__.split("."))}-path-hack.pth'
87
+
88
+
89
+ def _install_path_hack_file() -> None:
90
+ sp = site.getsitepackages()[0]
91
+ if os.path.isfile(fp := os.path.join(sp, _PATH_HACK_FILE_NAME)):
92
+ return
93
+
94
+ with open(fp, 'w') as f:
95
+ f.write(f'import {__name__}; {__name__}._activate_path_hack()')
96
+
97
+ _activate_path_hack()
98
+
99
+
100
+ ##
101
+
102
+
103
+ def setup_install_manager() -> None:
104
+ if detect_install_manager() is None:
105
+ return
106
+
107
+ _install_path_hack_file()
omdev/tools/gittools.py CHANGED
@@ -1,3 +1,5 @@
1
+ import os
2
+ import re
1
3
  import subprocess
2
4
 
3
5
  from omlish import argparse as ap
@@ -18,6 +20,46 @@ class Cli(ap.Cli):
18
20
  shell=True,
19
21
  )
20
22
 
23
+ #
24
+
25
+ @ap.command()
26
+ def commits_by_date(self) -> None:
27
+ subprocess.check_call(['git log --date=short --pretty=format:%ad | sort | uniq -c'], shell=True) # noqa
28
+
29
+ #
30
+
31
+ _GITHUB_PAT = re.compile(r'((http(s)?://)?(www\./)?github(\.com)?/)?(?P<user>[^/.]+)/(?P<repo>[^/.]+)(/.*)?')
32
+
33
+ @ap.command(
34
+ ap.arg('repo'),
35
+ ap.arg('args', nargs=ap.REMAINDER),
36
+ accepts_unknown=True,
37
+ )
38
+ def clone(self) -> None:
39
+ if not (m := self._GITHUB_PAT.fullmatch(self.args.repo)):
40
+ subprocess.check_call([
41
+ 'git',
42
+ 'clone',
43
+ *self.unknown_args,
44
+ *self.args.args,
45
+ self.args.repo,
46
+ ])
47
+ return
48
+
49
+ user = m.group('user')
50
+ repo = m.group('repo')
51
+
52
+ os.makedirs(user, 0o755, exist_ok=True)
53
+
54
+ subprocess.check_call([
55
+ 'git',
56
+ 'clone',
57
+ *self.unknown_args,
58
+ *self.args.args,
59
+ f'https://github.com/{user}/{repo}.git',
60
+ os.path.join(user, repo),
61
+ ])
62
+
21
63
 
22
64
  # @omlish-manifest
23
65
  _CLI_MODULE = CliModule('git', __name__)
omdev/tools/piptools.py CHANGED
@@ -66,7 +66,7 @@ class Cli(ap.Cli):
66
66
  dist_cn = canonicalize_name(dist.metadata['Name'], validate=True)
67
67
  if dist_cn in dists:
68
68
  # raise NameError(dist_cn)
69
- print(f'!! duplicate dist: {dist_cn}', file=sys.stderr)
69
+ # print(f'!! duplicate dist: {dist_cn}', file=sys.stderr)
70
70
  continue
71
71
 
72
72
  dists.add(dist_cn)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omdev
3
- Version: 0.0.0.dev43
3
+ Version: 0.0.0.dev45
4
4
  Summary: omdev
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,7 +12,7 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
13
  Requires-Python: ~=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omlish ==0.0.0.dev43
15
+ Requires-Dist: omlish ==0.0.0.dev45
16
16
  Provides-Extra: all
17
17
  Requires-Dist: pycparser ~=2.22 ; extra == 'all'
18
18
  Requires-Dist: cffi ~=1.17 ; extra == 'all'
@@ -1,4 +1,4 @@
1
- omdev/.manifests.json,sha256=c-eZ9NasJYDz6P7LeyEV_-NLPoVpI3ph8LcO8Hg0JNY,3921
1
+ omdev/.manifests.json,sha256=kEgqk5lxYhb_GAnksPc5f0xDcG-5eMUtdcT-v77bCzU,3918
2
2
  omdev/__about__.py,sha256=LqSNNFFcT84xW3W8fIOJ78kPYJKFLIXZyDX-AJREvN0,1005
3
3
  omdev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  omdev/bracepy.py,sha256=HwBK5XmlOsF_juTel25fRLJK9vHSJCWXuCc-OZlevRQ,2619
@@ -54,10 +54,11 @@ omdev/cexts/_distutils/compilers/ccompiler.py,sha256=cTs88qrvj0hBVXHfemSDE_du_nE
54
54
  omdev/cexts/_distutils/compilers/options.py,sha256=H7r5IcLvga5Fs3jjXWIT-6ap3JBduXRKgtpDmSGCZxs,3818
55
55
  omdev/cexts/_distutils/compilers/unixccompiler.py,sha256=o1h8QuyupLntv4F21_XjzAZmCiwwxJuTmOirvBSL-Qw,15419
56
56
  omdev/cli/__init__.py,sha256=V_l6VP1SZMlJbO-8CJwSuO9TThOy2S_oaPepNYgIrbE,37
57
- omdev/cli/__main__.py,sha256=d23loR_cKfTYZwYiqpt_CmKI7dd5WcYFgIYzqMep75E,68
58
- omdev/cli/clicmds.py,sha256=Qt75JOZ5nnI1OSvKFkNy25Zi1iaNx_hcPlK3PkgWgYY,1923
59
- omdev/cli/install.py,sha256=-pFczwyJn48IeqCswbJzrV15ZslQUNIcREiiOGSjDk0,3933
60
- omdev/cli/main.py,sha256=rGc8SIk-czOS9eILZbzu27nx3ezjc54fn880OWskpYE,1880
57
+ omdev/cli/__main__.py,sha256=5IeIERm-371fSI5ZvPv8eldAJBwgKwpR0R49pTsILNM,76
58
+ omdev/cli/clicli.py,sha256=NY4MF16007Ri31rUsq868u1V9JYDb_2atBALqjjXJK4,1624
59
+ omdev/cli/install.py,sha256=D-OlewQU3HJdt8F-u3C4o90Py5_XrFXhDxp_nXQvpko,4495
60
+ omdev/cli/main.py,sha256=opoOOc_d4yCfI0_dWkwDV7O2WSnoybIH3YsylGFwqLI,2039
61
+ omdev/cli/managers.py,sha256=k2IkanY-vO_zSJqMrSaak3KjAhrGAjoOLBerTrM9o-A,2855
61
62
  omdev/cli/types.py,sha256=7_Owg0P8C8oOObSuOp6aEYSjkEukVFxTT00SRy1bLHM,250
62
63
  omdev/interp/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
63
64
  omdev/interp/__main__.py,sha256=GMCqeGYltgt5dlJzHxY9gqisa8cRkrPfmZYuZnjg4WI,162
@@ -106,15 +107,15 @@ omdev/toml/parser.py,sha256=84bn09uhYHwQGyfww6Rw6y1RxPAE_HDltODOSakcqDM,29186
106
107
  omdev/toml/writer.py,sha256=lk3on3YXVbWuLJa-xsOzOhs1bBAT1vXqw4mBbluZl_w,3040
107
108
  omdev/tools/__init__.py,sha256=iVJAOQ0viGTQOm0DLX4uZLro-9jOioYJGLg9s0kDx1A,78
108
109
  omdev/tools/dockertools.py,sha256=x00GV8j1KReMXwxJ641GlcsVwHoWeuzdIKVBp36BqwU,5298
109
- omdev/tools/gittools.py,sha256=s_-cxh-4Nv2a_bJtHZ6AiKF8kx2IjDrt_GOH8Wjz-3M,669
110
+ omdev/tools/gittools.py,sha256=i2WFM2pX5riDJBchFXMmegOCuLSjvTkKC1ltqShSo7E,1773
110
111
  omdev/tools/importscan.py,sha256=usF35AjdMZacpe8nfP-wfzxelExT5sEQUORNcBKqr5M,3929
111
- omdev/tools/piptools.py,sha256=lhwzGXD-v0KFEQNyvzvdO2Kw1OA_2AfGPBs_rIkz8iE,2772
112
+ omdev/tools/piptools.py,sha256=-jR5q3w4sHqntxCLExFCBNIARB788FUsAbJ62PK2sBU,2774
112
113
  omdev/tools/proftools.py,sha256=xKSm_yPoCnfsvS3iT9MblDqFMuZmGfI3_koGj8amMyU,145
113
114
  omdev/tools/rst.py,sha256=6dWk8QZHoGiLSuBw3TKsXZjjFK6wWBEtPi9krdCLKKg,977
114
115
  omdev/tools/sqlrepl.py,sha256=tmFZh80-xsGM62dyQ7_UGLebChrj7IHbIPYBWDJMgVk,5741
115
- omdev-0.0.0.dev43.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
116
- omdev-0.0.0.dev43.dist-info/METADATA,sha256=5Hv8QOnRyAlCu0hmOy2f4I772ceLh29Q1XPQmufh8fw,1252
117
- omdev-0.0.0.dev43.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
118
- omdev-0.0.0.dev43.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
119
- omdev-0.0.0.dev43.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
120
- omdev-0.0.0.dev43.dist-info/RECORD,,
116
+ omdev-0.0.0.dev45.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
117
+ omdev-0.0.0.dev45.dist-info/METADATA,sha256=0VYbpYlLSNXLvBrxEpOdijqa8PlkH72pNoxql6wYM8Q,1252
118
+ omdev-0.0.0.dev45.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
119
+ omdev-0.0.0.dev45.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
120
+ omdev-0.0.0.dev45.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
121
+ omdev-0.0.0.dev45.dist-info/RECORD,,
omdev/cli/clicmds.py DELETED
@@ -1,83 +0,0 @@
1
- import argparse
2
- import inspect
3
- import os
4
- import subprocess
5
- import sys
6
-
7
- from omlish import __about__
8
-
9
- from . import install
10
- from .types import CliModule
11
-
12
-
13
- def _print_version(args) -> None:
14
- print(__about__.__version__)
15
-
16
-
17
- def _print_revision(args) -> None:
18
- print(__about__.__revision__)
19
-
20
-
21
- def _reinstall(args) -> None:
22
- mod_name = globals()['__spec__'].name
23
- tool_name = '.'.join([mod_name.partition('.')[0], 'tools', 'piptools'])
24
-
25
- out = subprocess.check_output([
26
- sys.executable,
27
- '-m',
28
- tool_name,
29
- 'list-root-dists',
30
- ]).decode()
31
-
32
- deps = sorted(
33
- ({s for l in out.splitlines() if (s := l.strip())} | set(args.extra_deps or []))
34
- - {install.DEFAULT_CLI_PKG} # noqa
35
- )
36
-
37
- if deps:
38
- print('Reinstalling with following additional dependencies:')
39
- print('\n'.join(' ' + d for d in deps))
40
- else:
41
- print('No additional dependencies detected.')
42
- print()
43
- print('Continue with reinstall? (ctrl-c to cancel)')
44
- input()
45
-
46
- install_src = inspect.getsource(install)
47
-
48
- os.execl(
49
- sys.executable,
50
- sys.executable,
51
- '-c',
52
- install_src,
53
- *deps,
54
- )
55
-
56
-
57
- # @omlish-manifest
58
- _CLI_MODULE = CliModule('cli', __name__)
59
-
60
-
61
- def _main(argv=None) -> None:
62
- parser = argparse.ArgumentParser()
63
- subparsers = parser.add_subparsers()
64
-
65
- parser_version = subparsers.add_parser('version')
66
- parser_version.set_defaults(func=_print_version)
67
-
68
- parser_revision = subparsers.add_parser('revision')
69
- parser_revision.set_defaults(func=_print_revision)
70
-
71
- parser_reinstall = subparsers.add_parser('reinstall')
72
- parser_reinstall.add_argument('extra_deps', nargs='*')
73
- parser_reinstall.set_defaults(func=_reinstall)
74
-
75
- args = parser.parse_args(argv)
76
- if not getattr(args, 'func', None):
77
- parser.print_help()
78
- else:
79
- args.func(args)
80
-
81
-
82
- if __name__ == '__main__':
83
- _main()