dk-tasklib 3.0.9__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.
- dk_tasklib-3.0.9.dist-info/METADATA +78 -0
- dk_tasklib-3.0.9.dist-info/RECORD +35 -0
- dk_tasklib-3.0.9.dist-info/WHEEL +5 -0
- dk_tasklib-3.0.9.dist-info/entry_points.txt +2 -0
- dk_tasklib-3.0.9.dist-info/top_level.txt +1 -0
- dktasklib/__init__.py +3 -0
- dktasklib/_version.py +1 -0
- dktasklib/clean.py +8 -0
- dktasklib/commands.py +95 -0
- dktasklib/concat.py +76 -0
- dktasklib/docs.py +225 -0
- dktasklib/entry_points/__init__.py +5 -0
- dktasklib/entry_points/confbase.py +172 -0
- dktasklib/entry_points/dktasklibcmd.py +102 -0
- dktasklib/entry_points/pytemplate.py +16 -0
- dktasklib/entry_points/taskbase.py +135 -0
- dktasklib/environment.py +11 -0
- dktasklib/executables.py +200 -0
- dktasklib/help.py +17 -0
- dktasklib/jstools.py +320 -0
- dktasklib/lessc.py +111 -0
- dktasklib/manage.py +71 -0
- dktasklib/npm.py +19 -0
- dktasklib/package/__init__.py +31 -0
- dktasklib/package/package_interface.py +103 -0
- dktasklib/pset.py +122 -0
- dktasklib/publish.py +50 -0
- dktasklib/rule.py +65 -0
- dktasklib/runners.py +40 -0
- dktasklib/upversion.py +165 -0
- dktasklib/urlinliner.py +90 -0
- dktasklib/utils.py +234 -0
- dktasklib/version.py +103 -0
- dktasklib/watch.py +90 -0
- dktasklib/wintask.py +4 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Configuration file for the Sphinx documentation builder.
|
|
2
|
+
#
|
|
3
|
+
# This file does only contain a selection of the most common options. For a
|
|
4
|
+
# full list see the documentation:
|
|
5
|
+
# http://www.sphinx-doc.org/en/stable/config
|
|
6
|
+
|
|
7
|
+
# -- Path setup --------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
# If extensions (or modules to document with autodoc) are in another directory,
|
|
10
|
+
# add these directories to sys.path here. If the directory is relative to the
|
|
11
|
+
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
|
12
|
+
#
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
sys.path.insert(0, os.path.abspath('..'))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# -- Project information -----------------------------------------------------
|
|
19
|
+
|
|
20
|
+
project = '##{PACKAGE}'
|
|
21
|
+
copyright = '##{YEAR}, ##{AUTHOR}'
|
|
22
|
+
author = '##{AUTHOR}'
|
|
23
|
+
|
|
24
|
+
# The short X.Y version
|
|
25
|
+
version = '##{VERSION}'
|
|
26
|
+
# The full version, including alpha/beta/rc tags
|
|
27
|
+
release = '##{VERSION}'
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# -- General configuration ---------------------------------------------------
|
|
31
|
+
|
|
32
|
+
# If your documentation needs a minimal Sphinx version, state it here.
|
|
33
|
+
#
|
|
34
|
+
# needs_sphinx = '1.0'
|
|
35
|
+
|
|
36
|
+
# Add any Sphinx extension module names here, as strings. They can be
|
|
37
|
+
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
|
38
|
+
# ones.
|
|
39
|
+
extensions = [
|
|
40
|
+
'sphinx.ext.autodoc',
|
|
41
|
+
'sphinx.ext.intersphinx',
|
|
42
|
+
'sphinx.ext.todo',
|
|
43
|
+
'sphinx.ext.viewcode',
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
# Add any paths that contain templates here, relative to this directory.
|
|
47
|
+
templates_path = ['_templates']
|
|
48
|
+
|
|
49
|
+
# The suffix(es) of source filenames.
|
|
50
|
+
# You can specify multiple suffix as a list of string:
|
|
51
|
+
#
|
|
52
|
+
# source_suffix = ['.rst', '.md']
|
|
53
|
+
source_suffix = '.rst'
|
|
54
|
+
|
|
55
|
+
# The master toctree document.
|
|
56
|
+
master_doc = 'index'
|
|
57
|
+
|
|
58
|
+
# The language for content autogenerated by Sphinx. Refer to documentation
|
|
59
|
+
# for a list of supported languages.
|
|
60
|
+
#
|
|
61
|
+
# This is also used if you do content translation via gettext catalogs.
|
|
62
|
+
# Usually you set "language" from the command line for these cases.
|
|
63
|
+
language = 'en'
|
|
64
|
+
|
|
65
|
+
# List of patterns, relative to source directory, that match files and
|
|
66
|
+
# directories to ignore when looking for source files.
|
|
67
|
+
# This pattern also affects html_static_path and html_extra_path .
|
|
68
|
+
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
|
69
|
+
|
|
70
|
+
# The name of the Pygments (syntax highlighting) style to use.
|
|
71
|
+
pygments_style = 'sphinx'
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# -- Options for HTML output -------------------------------------------------
|
|
75
|
+
|
|
76
|
+
# The theme to use for HTML and HTML Help pages. See the documentation for
|
|
77
|
+
# a list of builtin themes.
|
|
78
|
+
#
|
|
79
|
+
html_theme = 'alabaster'
|
|
80
|
+
|
|
81
|
+
# Theme options are theme-specific and customize the look and feel of a theme
|
|
82
|
+
# further. For a list of options available for each theme, see the
|
|
83
|
+
# documentation.
|
|
84
|
+
#
|
|
85
|
+
# html_theme_options = {}
|
|
86
|
+
|
|
87
|
+
# Add any paths that contain custom static files (such as style sheets) here,
|
|
88
|
+
# relative to this directory. They are copied after the builtin static files,
|
|
89
|
+
# so a file named "default.css" will overwrite the builtin "default.css".
|
|
90
|
+
html_static_path = ['_static']
|
|
91
|
+
|
|
92
|
+
# Custom sidebar templates, must be a dictionary that maps document names
|
|
93
|
+
# to template names.
|
|
94
|
+
#
|
|
95
|
+
# The default sidebars (for documents that don't match any pattern) are
|
|
96
|
+
# defined by theme itself. Builtin themes are using these templates by
|
|
97
|
+
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
|
|
98
|
+
# 'searchbox.html']``.
|
|
99
|
+
#
|
|
100
|
+
# html_sidebars = {}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# -- Options for HTMLHelp output ---------------------------------------------
|
|
104
|
+
|
|
105
|
+
# Output file base name for HTML help builder.
|
|
106
|
+
htmlhelp_basename = '##{PACKAGE}doc'
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# -- Options for LaTeX output ------------------------------------------------
|
|
110
|
+
|
|
111
|
+
latex_elements = {
|
|
112
|
+
# The paper size ('letterpaper' or 'a4paper').
|
|
113
|
+
#
|
|
114
|
+
# 'papersize': 'letterpaper',
|
|
115
|
+
|
|
116
|
+
# The font size ('10pt', '11pt' or '12pt').
|
|
117
|
+
#
|
|
118
|
+
# 'pointsize': '10pt',
|
|
119
|
+
|
|
120
|
+
# Additional stuff for the LaTeX preamble.
|
|
121
|
+
#
|
|
122
|
+
# 'preamble': '',
|
|
123
|
+
|
|
124
|
+
# Latex figure (float) alignment
|
|
125
|
+
#
|
|
126
|
+
# 'figure_align': 'htbp',
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# Grouping the document tree into LaTeX files. List of tuples
|
|
130
|
+
# (source start file, target name, title,
|
|
131
|
+
# author, documentclass [howto, manual, or own class]).
|
|
132
|
+
latex_documents = [
|
|
133
|
+
(master_doc, '##{PACKAGE}.tex', '##{PACKAGE} Documentation',
|
|
134
|
+
'##{AUTHOR}', 'manual'),
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# -- Options for manual page output ------------------------------------------
|
|
139
|
+
|
|
140
|
+
# One entry per manual page. List of tuples
|
|
141
|
+
# (source start file, name, description, authors, manual section).
|
|
142
|
+
man_pages = [
|
|
143
|
+
(master_doc, '##{PACKAGE}', '##{PACKAGE} Documentation',
|
|
144
|
+
[author], 1)
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# -- Options for Texinfo output ----------------------------------------------
|
|
149
|
+
|
|
150
|
+
# Grouping the document tree into Texinfo files. List of tuples
|
|
151
|
+
# (source start file, target name, title, author,
|
|
152
|
+
# dir menu entry, description, category)
|
|
153
|
+
texinfo_documents = [
|
|
154
|
+
(master_doc, '##{PACKAGE}', '##{PACKAGE} Documentation',
|
|
155
|
+
author, '##{PACKAGE}', 'One line description of project.',
|
|
156
|
+
'Miscellaneous'),
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
# -- Extension configuration -------------------------------------------------
|
|
161
|
+
|
|
162
|
+
# -- Options for intersphinx extension ---------------------------------------
|
|
163
|
+
|
|
164
|
+
# Example configuration for intersphinx: refer to the Python standard library.
|
|
165
|
+
intersphinx_mapping = {
|
|
166
|
+
'python': ('https://docs.python.org/3', None),
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# -- Options for todo extension ----------------------------------------------
|
|
170
|
+
|
|
171
|
+
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
|
172
|
+
todo_include_todos = True
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Commands installed by setup.py
|
|
2
|
+
"""
|
|
3
|
+
# pragma: nocover
|
|
4
|
+
import argparse
|
|
5
|
+
import datetime
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
import textwrap
|
|
10
|
+
|
|
11
|
+
from dkfileutils.path import Path
|
|
12
|
+
from .._version import __version__
|
|
13
|
+
from .pytemplate import PyTemplate
|
|
14
|
+
from ..runners import run
|
|
15
|
+
|
|
16
|
+
DIRNAME = Path(os.path.dirname(__file__))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def install_cmd(args):
|
|
20
|
+
"""Install a basic task.py to the current directory.
|
|
21
|
+
"""
|
|
22
|
+
cwd = Path.curdir()
|
|
23
|
+
tasks_file = cwd / 'tasks.py'
|
|
24
|
+
if tasks_file.exists() and not args.force:
|
|
25
|
+
print("tasks.py exists (use --force to overwrite)")
|
|
26
|
+
sys.exit(1)
|
|
27
|
+
taskbase = DIRNAME / 'taskbase.py'
|
|
28
|
+
txt = taskbase.read('rb')
|
|
29
|
+
txt = txt.replace(b'\r\n', b'\n')
|
|
30
|
+
t = PyTemplate(txt)
|
|
31
|
+
tasks_file.write(t.substitute(
|
|
32
|
+
VERSION=__version__
|
|
33
|
+
))
|
|
34
|
+
if args.django:
|
|
35
|
+
add_django_to_docs_conf()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def create_docs_cmd(args):
|
|
39
|
+
cwd = Path.curdir()
|
|
40
|
+
docsdir = cwd / 'docs'
|
|
41
|
+
if docsdir.exists() and not args.force:
|
|
42
|
+
print("docs directory exists (use --force to overwrite)")
|
|
43
|
+
sys.exit(1)
|
|
44
|
+
confbase = DIRNAME / 'confbase.py'
|
|
45
|
+
txt = confbase.read('rb')
|
|
46
|
+
txt = txt.replace(b'\r\n', b'\n')
|
|
47
|
+
t = PyTemplate(txt)
|
|
48
|
+
|
|
49
|
+
docsdir.makedirs()
|
|
50
|
+
(docsdir / 'conf.py').write(t.substitute(
|
|
51
|
+
VERSION=run('python setup.py --version'),
|
|
52
|
+
PACKAGE=run('python setup.py --name'),
|
|
53
|
+
YEAR=datetime.date.today().year,
|
|
54
|
+
AUTHOR=run('python setup.py --author').strip()
|
|
55
|
+
))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def add_django_to_docs_conf():
|
|
59
|
+
cwd = Path.curdir()
|
|
60
|
+
conf_file = cwd / 'docs' / 'conf.py'
|
|
61
|
+
if 'django.setup()' in conf_file.read():
|
|
62
|
+
print("./docs/conf.py already contains django.setup()")
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
src = conf_file.read('r')
|
|
66
|
+
pre, post = src.split('\n\n', 1)
|
|
67
|
+
conf_file.write(
|
|
68
|
+
pre + "\n\n" +
|
|
69
|
+
textwrap.dedent("""\
|
|
70
|
+
import django
|
|
71
|
+
django.setup()
|
|
72
|
+
""") + "\n\n" +
|
|
73
|
+
post
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def main(args=None):
|
|
78
|
+
args = args or sys.argv[1:]
|
|
79
|
+
p = argparse.ArgumentParser()
|
|
80
|
+
commands = list(sorted([name[:-4] for name in globals()
|
|
81
|
+
if name.endswith('_cmd')]))
|
|
82
|
+
|
|
83
|
+
p.add_argument('command', help="run command (available commands: %s)" % ', '.join(commands))
|
|
84
|
+
p.add_argument('--version', action='version', version='%(prog)s ' + __version__)
|
|
85
|
+
p.add_argument('--verbose', '-v', action='store_true', help="verbose output")
|
|
86
|
+
p.add_argument('--force', '-f', action='store_true', help="force execution of commands.")
|
|
87
|
+
p.add_argument('--django', '-d', action='store_true', help="make django specific changes.")
|
|
88
|
+
|
|
89
|
+
args = p.parse_args(args)
|
|
90
|
+
|
|
91
|
+
if args.verbose:
|
|
92
|
+
print("ARGS:", args)
|
|
93
|
+
|
|
94
|
+
if args.command not in commands:
|
|
95
|
+
print("Unknown command:", args.command)
|
|
96
|
+
sys.exit(1)
|
|
97
|
+
|
|
98
|
+
globals()[args.command + '_cmd'](args)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
main()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from string import Template
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PyTemplate(Template):
|
|
5
|
+
"""
|
|
6
|
+
Template strings that can replace ##{PATTERN} instances.
|
|
7
|
+
"""
|
|
8
|
+
def __init__(self, t):
|
|
9
|
+
if isinstance(t, bytes):
|
|
10
|
+
t = t.decode('u8')
|
|
11
|
+
super().__init__(t.replace('$', '$$').replace('##{', '${'))
|
|
12
|
+
|
|
13
|
+
def substitute(self, *args, **kw):
|
|
14
|
+
return super().substitute(
|
|
15
|
+
**{k.upper(): v for k, v in kw.items()}
|
|
16
|
+
)
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base version of package/tasks.py, created by version ##{VERSION} of
|
|
3
|
+
|
|
4
|
+
package/root/dir> dk-tasklib install
|
|
5
|
+
|
|
6
|
+
(it should reside in the root directory of your package)
|
|
7
|
+
|
|
8
|
+
This file defines tasks for the Invoke tool: http://www.pyinvoke.org
|
|
9
|
+
|
|
10
|
+
Basic usage::
|
|
11
|
+
|
|
12
|
+
inv -l # list all available tasks
|
|
13
|
+
inv build -f # build everything, forcefully
|
|
14
|
+
inv build --docs # only build the docs
|
|
15
|
+
|
|
16
|
+
dk-tasklib is a library of basic tasks that tries to automate common tasks.
|
|
17
|
+
dk-tasklib will attempt to install any tools/libraries/etc. that are required,
|
|
18
|
+
e.g. when running the task to compile x.less to x.css, it will check that
|
|
19
|
+
the lessc compiler is installed (and if not it will attempt to install it).
|
|
20
|
+
|
|
21
|
+
This file is an initial skeleton, you are supposed to edit and add to it so it
|
|
22
|
+
will fit your use case.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
# pragma: nocover
|
|
27
|
+
import os
|
|
28
|
+
import warnings
|
|
29
|
+
|
|
30
|
+
from dkfileutils.changed import changed
|
|
31
|
+
from dkfileutils.path import Path
|
|
32
|
+
from dktasklib.wintask import task
|
|
33
|
+
from invoke import Collection
|
|
34
|
+
|
|
35
|
+
from dktasklib import docs as doctools
|
|
36
|
+
from dktasklib import lessc
|
|
37
|
+
from dktasklib import version, upversion
|
|
38
|
+
from dktasklib.manage import collectstatic
|
|
39
|
+
from dktasklib.package import Package, package
|
|
40
|
+
from dktasklib.watch import Watcher
|
|
41
|
+
# from dktasklib.publish import publish
|
|
42
|
+
|
|
43
|
+
#: where tasks.py is located (root of package)
|
|
44
|
+
DIRNAME = Path(os.path.dirname(__file__))
|
|
45
|
+
|
|
46
|
+
# collectstatic
|
|
47
|
+
# --------------
|
|
48
|
+
# Specify which settings file should be used when running
|
|
49
|
+
# `python manage.py collectstatic` (must be on the path or package root
|
|
50
|
+
# directory).
|
|
51
|
+
DJANGO_SETTINGS_MODULE = ''
|
|
52
|
+
|
|
53
|
+
# .less
|
|
54
|
+
# ------
|
|
55
|
+
# there should be a mypkg/mypkg/less/mypkg.less file that imports any other
|
|
56
|
+
# needed sources
|
|
57
|
+
|
|
58
|
+
# ============================================================================
|
|
59
|
+
# autodoc is in a separate process, so can't use settings.configure().
|
|
60
|
+
HAVE_SETTINGS = bool(DJANGO_SETTINGS_MODULE)
|
|
61
|
+
if not HAVE_SETTINGS and (DIRNAME / 'settings.py').exists():
|
|
62
|
+
# look for a dummy settings.py module in the root of the package.
|
|
63
|
+
DJANGO_SETTINGS_MODULE = 'settings'
|
|
64
|
+
if DJANGO_SETTINGS_MODULE:
|
|
65
|
+
os.environ['DJANGO_SETTINGS_MODULE'] = DJANGO_SETTINGS_MODULE
|
|
66
|
+
WARN_ABOUT_SETTINGS = not bool(DJANGO_SETTINGS_MODULE)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@task
|
|
70
|
+
def build(ctx, less=False, docs=False, force=False):
|
|
71
|
+
"""Build everything and collectstatic.
|
|
72
|
+
"""
|
|
73
|
+
specified = any([less, docs])
|
|
74
|
+
buildall = not specified
|
|
75
|
+
|
|
76
|
+
if buildall or less:
|
|
77
|
+
less_fname = ctx.pkg.source_less / ctx.pkg.name + '.less'
|
|
78
|
+
if less_fname.exists():
|
|
79
|
+
lessc.LessRule(
|
|
80
|
+
ctx,
|
|
81
|
+
src='{pkg.source_less}/{pkg.name}.less',
|
|
82
|
+
dst='{pkg.django_static}/{pkg.name}/css/{pkg.name}-{version}.min.css',
|
|
83
|
+
force=force
|
|
84
|
+
)
|
|
85
|
+
elif less:
|
|
86
|
+
warnings.warn(
|
|
87
|
+
"WARNING: build --less specified, but no file at: " + less_fname
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
if buildall or docs:
|
|
91
|
+
if WARN_ABOUT_SETTINGS:
|
|
92
|
+
warnings.warn(
|
|
93
|
+
"autodoc might need a dummy settings file in the root of "
|
|
94
|
+
"your package. Since it runs in a separate process you cannot"
|
|
95
|
+
"use settings.configure()"
|
|
96
|
+
)
|
|
97
|
+
doctools.build(ctx, force=force)
|
|
98
|
+
|
|
99
|
+
if HAVE_SETTINGS and (force or changed(ctx.pkg.django_static)):
|
|
100
|
+
collectstatic(ctx, DJANGO_SETTINGS_MODULE, force=force)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@task
|
|
104
|
+
def watch(ctx):
|
|
105
|
+
"""Automatically run build whenever a relevant file changes.
|
|
106
|
+
"""
|
|
107
|
+
watcher = Watcher(ctx)
|
|
108
|
+
watcher.watch_directory(
|
|
109
|
+
path='{pkg.source_less}', ext='.less',
|
|
110
|
+
action=lambda e: build(ctx, less=True)
|
|
111
|
+
)
|
|
112
|
+
watcher.watch_directory(
|
|
113
|
+
path='{pkg.docs}', ext='.rst',
|
|
114
|
+
action=lambda e: build(ctx, docs=True)
|
|
115
|
+
)
|
|
116
|
+
watcher.start()
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# individual tasks that can be run from this project
|
|
120
|
+
ns = Collection(
|
|
121
|
+
build,
|
|
122
|
+
watch,
|
|
123
|
+
lessc,
|
|
124
|
+
doctools,
|
|
125
|
+
version, upversion,
|
|
126
|
+
package,
|
|
127
|
+
collectstatic,
|
|
128
|
+
# publish,
|
|
129
|
+
)
|
|
130
|
+
ns.configure({
|
|
131
|
+
'pkg': Package(),
|
|
132
|
+
'run': {
|
|
133
|
+
'echo': True
|
|
134
|
+
}
|
|
135
|
+
})
|
dktasklib/environment.py
ADDED
dktasklib/executables.py
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
4
|
+
import invoke
|
|
5
|
+
from dkfileutils.which import get_executable
|
|
6
|
+
from dktasklib import Package
|
|
7
|
+
from dktasklib.utils import win32
|
|
8
|
+
from dktasklib import runners
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MissingCommand(Exception):
|
|
12
|
+
"""Exception thrown when a command (executable) is not found.
|
|
13
|
+
"""
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# noinspection PyShadowingNames
|
|
18
|
+
class Executables:
|
|
19
|
+
"""Class for finding executables on the host system.
|
|
20
|
+
"""
|
|
21
|
+
def __init__(self):
|
|
22
|
+
self._cache = {}
|
|
23
|
+
self._ctx = None
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def ctx(self):
|
|
27
|
+
if self._ctx is None:
|
|
28
|
+
self._ctx = invoke.Context()
|
|
29
|
+
return self._ctx
|
|
30
|
+
|
|
31
|
+
def require(self, *dependencies):
|
|
32
|
+
"""Ensure that all dependencies are available.
|
|
33
|
+
You should not need to call this yourself, use the :func:`requires`
|
|
34
|
+
decorator instead.
|
|
35
|
+
"""
|
|
36
|
+
for dep in dependencies:
|
|
37
|
+
self.find(dep)
|
|
38
|
+
|
|
39
|
+
def find(self, name, requires=(), install_txt=''):
|
|
40
|
+
"""Find the executable named ``name`` on the :envvar:`PATH`.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
name (str):
|
|
44
|
+
name of executable to find.
|
|
45
|
+
requires (List[str]):
|
|
46
|
+
list of executables to find first.
|
|
47
|
+
install_txt (str):
|
|
48
|
+
instructions for how to install the
|
|
49
|
+
executable if it is not found.
|
|
50
|
+
"""
|
|
51
|
+
if name not in self._cache:
|
|
52
|
+
self.require(*requires)
|
|
53
|
+
|
|
54
|
+
if hasattr(self, 'find_' + name):
|
|
55
|
+
self._cache[name] = getattr(self, 'find_' + name)()
|
|
56
|
+
else:
|
|
57
|
+
self._cache[name] = self._find_exe(name, requires, install_txt)
|
|
58
|
+
return self._cache[name]
|
|
59
|
+
|
|
60
|
+
def _find_exe(self, name, requires=(), install_txt=None):
|
|
61
|
+
fexe = get_executable(name)
|
|
62
|
+
if not fexe: # pragma: nocover
|
|
63
|
+
# try $PKG/node_modules/.bin/<exe|cmd>
|
|
64
|
+
noderoot = Package().root / 'node_modules/.bin'
|
|
65
|
+
if name + '.cmd' in noderoot:
|
|
66
|
+
return str(noderoot / (name + '.cmd'))
|
|
67
|
+
|
|
68
|
+
if not install_txt: # pragma: nocover
|
|
69
|
+
install_txt = "Missing command: %r" % name
|
|
70
|
+
if requires:
|
|
71
|
+
install_txt += " [requires: %s]" % requires
|
|
72
|
+
raise MissingCommand(install_txt)
|
|
73
|
+
return fexe
|
|
74
|
+
|
|
75
|
+
def find_wheel(self):
|
|
76
|
+
exename = 'wheel'
|
|
77
|
+
exepath = get_executable(exename)
|
|
78
|
+
if not exepath:
|
|
79
|
+
pip = get_executable('pip')
|
|
80
|
+
cmd = pip + ' install wheel[signatures]'
|
|
81
|
+
if win32:
|
|
82
|
+
runners.run(cmd)
|
|
83
|
+
exepath = get_executable(exename)
|
|
84
|
+
# generate signing key if downloading wheel
|
|
85
|
+
runners.run(exepath + ' keygen')
|
|
86
|
+
else:
|
|
87
|
+
raise MissingCommand("Missing wheel (%s)" % cmd)
|
|
88
|
+
print('Your ~/.pypirc file should have a [pypi] section instead of a [server-login] section')
|
|
89
|
+
return exepath
|
|
90
|
+
|
|
91
|
+
def find_twine(self):
|
|
92
|
+
exename = 'twine'
|
|
93
|
+
exepath = get_executable(exename)
|
|
94
|
+
if not exepath:
|
|
95
|
+
pip = get_executable('pip')
|
|
96
|
+
cmd = pip + ' install twine'
|
|
97
|
+
if win32:
|
|
98
|
+
runners.run(cmd)
|
|
99
|
+
exepath = get_executable(exename)
|
|
100
|
+
else:
|
|
101
|
+
raise MissingCommand("Missing twine (%s)" % cmd)
|
|
102
|
+
print('Your ~/.pypirc file should have a [pypi] section instead of a [server-login] section')
|
|
103
|
+
return exepath
|
|
104
|
+
|
|
105
|
+
def find_uglify(self):
|
|
106
|
+
exename = 'uglifyjs'
|
|
107
|
+
exepath = get_executable(exename)
|
|
108
|
+
if not exepath:
|
|
109
|
+
npminstall = "npm install -g uglify-js --no-color"
|
|
110
|
+
if win32:
|
|
111
|
+
self.ctx.run(npminstall, echo=False, encoding="utf-8")
|
|
112
|
+
exepath = get_executable(exename)
|
|
113
|
+
else:
|
|
114
|
+
raise MissingCommand("Missing uglifyjs (%s)" % npminstall)
|
|
115
|
+
return exepath
|
|
116
|
+
|
|
117
|
+
def find_browserify(self):
|
|
118
|
+
exename = 'browserify'
|
|
119
|
+
exepath = get_executable(exename)
|
|
120
|
+
npminstall = "npm install -g browserify --no-color"
|
|
121
|
+
if not exepath:
|
|
122
|
+
if win32:
|
|
123
|
+
self.ctx.run(npminstall, echo=True, encoding="utf-8")
|
|
124
|
+
exepath = get_executable(exename)
|
|
125
|
+
else:
|
|
126
|
+
raise MissingCommand("Missing browserify (%s)" % npminstall)
|
|
127
|
+
return exepath
|
|
128
|
+
|
|
129
|
+
def find_babili(self):
|
|
130
|
+
exename = 'babili'
|
|
131
|
+
exepath = get_executable(exename)
|
|
132
|
+
npminstall = "npm install -g babili --no-color"
|
|
133
|
+
if not exepath:
|
|
134
|
+
if win32:
|
|
135
|
+
self.ctx.run(npminstall, echo=False, encoding="utf-8")
|
|
136
|
+
exepath = get_executable(exename)
|
|
137
|
+
else:
|
|
138
|
+
raise MissingCommand("Missing babili (%s)" % npminstall)
|
|
139
|
+
return exepath
|
|
140
|
+
|
|
141
|
+
def find_babel(self):
|
|
142
|
+
exename = 'babel'
|
|
143
|
+
exepath = get_executable(exename)
|
|
144
|
+
npminstall = "npm install -g babel-cli --no-color"
|
|
145
|
+
if not exepath:
|
|
146
|
+
if win32:
|
|
147
|
+
self.ctx.run(npminstall, echo=False, encoding="utf-8")
|
|
148
|
+
exepath = get_executable(exename)
|
|
149
|
+
else:
|
|
150
|
+
raise MissingCommand("Missing babel (%s)" % npminstall)
|
|
151
|
+
return exepath
|
|
152
|
+
|
|
153
|
+
def find_nodejs(self): # pragma: nocover
|
|
154
|
+
"""Find :program:`node`.
|
|
155
|
+
"""
|
|
156
|
+
if sys.platform == 'win32':
|
|
157
|
+
node_exe = get_executable('node')
|
|
158
|
+
else:
|
|
159
|
+
node_exe = get_executable('nodejs') or get_executable('node')
|
|
160
|
+
|
|
161
|
+
if not node_exe: # pragma: nocover
|
|
162
|
+
raise MissingCommand("""
|
|
163
|
+
Install Node.js using your OS package manager
|
|
164
|
+
https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager
|
|
165
|
+
""")
|
|
166
|
+
return node_exe
|
|
167
|
+
|
|
168
|
+
def find_npm(self):
|
|
169
|
+
"""Find the node package manager (:program:`npm`).
|
|
170
|
+
"""
|
|
171
|
+
npm_exe = get_executable('npm')
|
|
172
|
+
if not npm_exe: # pragma: nocover
|
|
173
|
+
raise MissingCommand("""
|
|
174
|
+
Install Node.js using your OS package manager
|
|
175
|
+
https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager
|
|
176
|
+
""")
|
|
177
|
+
return npm_exe
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
#: public interface to the :py:class:`Executables` class
|
|
181
|
+
exe = Executables()
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def requires(*deps):
|
|
185
|
+
"""Decorator to declare global dependencies/requirements.
|
|
186
|
+
|
|
187
|
+
Usage (``@task`` must be last)::
|
|
188
|
+
|
|
189
|
+
@requires('nodejs', 'npm', 'lessc')
|
|
190
|
+
@task
|
|
191
|
+
def mytask(..)
|
|
192
|
+
|
|
193
|
+
"""
|
|
194
|
+
def _wrapper(fn):
|
|
195
|
+
try:
|
|
196
|
+
exe.require(*deps)
|
|
197
|
+
except MissingCommand as e:
|
|
198
|
+
warnings.warn(str(e))
|
|
199
|
+
return fn
|
|
200
|
+
return _wrapper
|
dktasklib/help.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from dktasklib.wintask import task
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@task(default=True)
|
|
5
|
+
def help(ctx):
|
|
6
|
+
"""
|
|
7
|
+
"""
|
|
8
|
+
ctx.run('invoke --help')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@task(default=True)
|
|
12
|
+
def list(ctx):
|
|
13
|
+
"""Show help, basically an alias for --help.
|
|
14
|
+
This task can be removed once the fix to this issue is released:
|
|
15
|
+
https://github.com/pyinvoke/invoke/issues/180
|
|
16
|
+
"""
|
|
17
|
+
ctx.run('invoke --list')
|