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.
dktasklib/jstools.py ADDED
@@ -0,0 +1,320 @@
1
+ import os
2
+ import textwrap
3
+
4
+ from dktasklib.wintask import task
5
+ from invoke import Collection
6
+ from dktasklib import runners
7
+ from dktasklib.commands import Command
8
+ from dktasklib.executables import requires
9
+ from dktasklib.utils import cd, switch_extension
10
+ from dktasklib.version import copy_to_version
11
+
12
+
13
+ def ensure_package_json(ctx):
14
+ """Is this a node package?
15
+ """
16
+ package_json = os.path.join(ctx.pkg.root, 'package.json')
17
+ if not os.path.exists(package_json):
18
+ print("Missing package.json file, creating default version..")
19
+ with cd(ctx.pkg.root):
20
+ ctx.run("npm init -f")
21
+ else:
22
+ return True
23
+
24
+
25
+ def ensure_babelrc(ctx):
26
+ """babel needs a .babelrc file to do any work.
27
+ """
28
+ babelrc = os.path.join(ctx.pkg.root, '.babelrc')
29
+ if not os.path.exists(babelrc):
30
+ print('Misssing %s (creating default version)' % babelrc)
31
+ with open(babelrc, 'w') as fp:
32
+ fp.write(textwrap.dedent("""
33
+ {
34
+ "presets": [
35
+ ["env", {
36
+ "targets": {
37
+ "browsers": [
38
+ "last 2 versions",
39
+ "IE >= 11"
40
+ ],
41
+ "useBuiltIns": true,
42
+ "node": "current"
43
+ }
44
+ }]
45
+ ],
46
+ "ignore": [
47
+ "node_modules"
48
+ ]
49
+ }
50
+ """))
51
+ else:
52
+ return True
53
+
54
+
55
+ def ensure_node_modules(ctx):
56
+ """Has node init been called? (if not call it).
57
+ """
58
+ node_modules = os.path.join(ctx.pkg.root, 'node_modules')
59
+ if not os.path.exists(node_modules):
60
+ with cd(ctx.pkg.root):
61
+ ctx.run("npm install --no-color")
62
+ else:
63
+ return True
64
+
65
+
66
+ def ensure_es2015(ctx):
67
+ if 'babel-preset-es2015' not in runners.run("npm ls --depth=0 babel-preset-es2015 --no-color"):
68
+ print("didn't find babel-preset-es2015, installing it..")
69
+ with cd(ctx.pkg.root):
70
+ ctx.run("npm install babel-preset-es2015 --save-dev")
71
+ else:
72
+ return True
73
+
74
+
75
+ def ensure_preset_es2016(ctx):
76
+ if 'babel-preset-es2015' not in runners.run("npm ls --depth=0 babel-preset-es2016 --no-color"):
77
+ print("didn't find babel-preset-es2016, installing it..")
78
+ with cd(ctx.pkg.root):
79
+ ctx.run("npm install babel-preset-es2016 --save-dev")
80
+ else:
81
+ return True
82
+
83
+
84
+ def ensure_preset_es2017(ctx):
85
+ if 'babel-preset-es2017' not in runners.run("npm ls --depth=0 babel-preset-es2017 --no-color"):
86
+ print("didn't find babel-preset-es2017, installing it..")
87
+ with cd(ctx.pkg.root):
88
+ ctx.run("npm install babel-preset-es2017 --save-dev")
89
+ else:
90
+ return True
91
+
92
+
93
+ def ensure_preset_latest(ctx):
94
+ if 'babel-preset-env' not in runners.run("npm ls --depth=0 babel-preset-env --no-color"):
95
+ print("didn't find babel-preset-env, installing it..")
96
+ with cd(ctx.pkg.root):
97
+ ctx.run("npm install babel-preset-env --save-dev")
98
+ else:
99
+ return True
100
+
101
+
102
+ def ensure_preset_babili(ctx):
103
+ if 'babel-preset-babili' not in runners.run("npm ls --depth=0 babel-preset-babili --no-color"):
104
+ print("didn't find babel-preset-babili, installing it..")
105
+ with cd(ctx.pkg.root):
106
+ ctx.run("npm install babel-preset-babili --save-dev")
107
+ else:
108
+ return True
109
+
110
+
111
+ def ensure_babelify(ctx):
112
+ if 'babelify' not in runners.run("npm ls --depth=0 babelify --no-color"):
113
+ print("didn't find babelify, installing it..")
114
+ with cd(ctx.pkg.root):
115
+ ctx.run("npm install --save-dev babelify --no-color", echo=False, encoding='utf-8')
116
+ else:
117
+ return True
118
+
119
+
120
+ def ensure_babel_minify(ctx):
121
+ nodepkgs = runners.run("npm ls --depth=0 babelify --no-color")
122
+ if 'babel-minify' not in nodepkgs and 'minify' not in nodepkgs:
123
+ print("didn't find babelify, installing it..")
124
+ with cd(ctx.pkg.root):
125
+ ctx.run("npm install --save-dev babelify --no-color", echo=False, encoding='utf-8')
126
+ else:
127
+ return True
128
+
129
+
130
+ def version_js(ctx, fname, kind='pkg', force=False):
131
+ """Add version number to a .js file.
132
+ """
133
+ dst = copy_to_version(
134
+ ctx,
135
+ fname,
136
+ kind=kind,
137
+ force=force
138
+ )
139
+ return dst
140
+
141
+
142
+ @requires('nodejs', 'npm', 'browserify')
143
+ @task
144
+ def browserify(ctx,
145
+ source, dest,
146
+ babelify=False,
147
+ require=(),
148
+ external=(),
149
+ entry=None):
150
+ """
151
+ Run ``browserify``
152
+
153
+ Args:
154
+ ctx (pyinvoke.Context): context
155
+ source (str): root source file
156
+ dest (str): path/name of assembled file
157
+ babelify (Bool): use babel transform
158
+ require (iterable): A module name or file to bundle.require()
159
+ Optionally use a colon separator to set the target.
160
+ external: Reference a file from another bundle. Files can be globs.
161
+ entry:
162
+
163
+ Returns:
164
+ None
165
+
166
+ """
167
+ print('ensure package.json:', ensure_package_json(ctx))
168
+ print('ensure node_modules:', ensure_node_modules(ctx))
169
+
170
+ options = "" # no source maps
171
+ if babelify:
172
+ print('ensure babelify:', ensure_babelify(ctx))
173
+ print('ensure preset latest:', ensure_preset_latest(ctx))
174
+ options += ' -t babelify'
175
+ options += ' --presets env'
176
+ for r in require:
177
+ options += ' -r "%s"' % r
178
+ for e in external:
179
+ options += ' -x "%s"' % e
180
+ if entry:
181
+ options += ' -e "%s"' % entry
182
+ cmd = "browserify {source} -o {dest} {options}".format(**locals())
183
+ ctx.run(cmd)
184
+ with open(dest, 'rb') as fp:
185
+ txt = fp.read()
186
+ if b'\r\n' in txt:
187
+ with open(dest, 'wb') as fp:
188
+ fp.write(txt.replace(b'\r\n', b'\n'))
189
+ return dest
190
+
191
+
192
+ babilicmd = Command('babili', '{src} {opts} -o {dst}',
193
+ requirements=('nodejs', 'npm', 'babili'))
194
+
195
+
196
+ @requires('nodejs', 'npm', 'babili')
197
+ @task
198
+ def babili(ctx, src, dst):
199
+ babilicmd(
200
+ ctx,
201
+ src=src,
202
+ dst=dst,
203
+ )
204
+ return dst
205
+
206
+
207
+ # babel-minify is linked to minify
208
+ babel_minifycmd = Command('babel-minify', '{src} {opts} -o {dst}',
209
+ requirements=('nodejs', 'npm', 'babel-minify'))
210
+
211
+
212
+ @requires('nodejs', 'npm', 'babel-minify')
213
+ @task
214
+ def babel_minify(ctx, src, dst):
215
+ """
216
+ dkjs/node_modules/.bin> babel-minify --help
217
+
218
+ Usage: minify index.js [options]
219
+
220
+ IO Options:
221
+ --out-file, -o Output to a specific file
222
+ --out-dir, -d Output to a specific directory
223
+
224
+ Transform Options:
225
+ --mangle Context and scope aware variable renaming
226
+ --simplify Simplifies code for minification by reducing statements into
227
+ expressions
228
+ --booleans Transform boolean literals into !0 for true and !1 for false
229
+ --builtIns Minify standard built-in objects
230
+ --consecutiveAdds Inlines consecutive property assignments, array pushes, etc.
231
+ --deadcode Inlines bindings and tries to evaluate expressions.
232
+ --evaluate Tries to evaluate expressions and inline the result. Deals
233
+ with numbers and strings
234
+ --flipComparisons Optimize code for repetition-based compression algorithms
235
+ such as gzip.
236
+ --infinity Minify Infinity to 1/0
237
+ --memberExpressions Convert valid member expression property literals into plain
238
+ identifiers
239
+ --mergeVars Merge sibling variables into single variable declaration
240
+ --numericLiterals Shortening of numeric literals via scientific notation
241
+ --propertyLiterals Transform valid identifier property key literals into identifiers
242
+ --regexpConstructors Change RegExp constructors into literals
243
+ --removeConsole Removes all console.* calls
244
+ --removeDebugger Removes all debugger statements
245
+ --removeUndefined Removes rval's for variable assignments, return arguments from
246
+ functions that evaluate to undefined
247
+ --replace Replaces matching nodes in the tree with a given replacement node
248
+ --simplifyComparisons Convert === and !== to == and != if their types are inferred
249
+ to be the same
250
+ --typeConstructors Minify constructors to equivalent version
251
+ --undefinedToVoid Transforms undefined into void 0
252
+
253
+ Other Options:
254
+ --keepFnName Preserve Function Name (useful for code depending on fn.name)
255
+ --keepClassName Preserve Class Name (useful for code depending on c.name)
256
+ --keepFnArgs Don't remove unused fn arguments (useful for code depending on fn.length)
257
+ --tdz Detect usages of variables in the Temporal Dead Zone
258
+
259
+ Nested Options:
260
+ To use nested options (plugin specfic options) simply use the pattern
261
+ --pluginName.featureName.
262
+
263
+ For example,
264
+ minify index.js --mangle.keepClassName --deadcode.keepFnArgs --outFile index.min.js
265
+ """
266
+
267
+ babel_minifycmd(
268
+ ctx,
269
+ src=src,
270
+ dst=dst,
271
+ keepFnName=True,
272
+ keepClassName=True,
273
+ mangle=False,
274
+ simplify=False,
275
+ builtIns=True,
276
+ deadcode=False,
277
+ evaluate=False
278
+ )
279
+ return dst
280
+
281
+
282
+ uglifycmd = Command('uglifyjs', '{src} {opts} -o {dst}',
283
+ requirements=('nodejs', 'npm', 'uglify'))
284
+
285
+
286
+ @requires('nodejs', 'npm', 'uglify')
287
+ @task
288
+ def uglifyjs(ctx,
289
+ src, dst,
290
+ compress=True, mangle=True):
291
+ uglifycmd(
292
+ ctx,
293
+ src=src,
294
+ dst=dst,
295
+ compress=compress,
296
+ mangle=mangle
297
+ )
298
+ return dst
299
+
300
+
301
+ @task(default=True)
302
+ def buildjs(ctx, src, dst, force=False, **kw):
303
+ uglify = kw.pop('uglify', False)
304
+ if kw.pop('browserify', False):
305
+ dst = browserify(ctx, src, dst,
306
+ babelify=kw.pop('babelify', src.endswith('.jsx')), **kw)
307
+
308
+ if uglify:
309
+ finaldst = switch_extension(dst, '.min.js')
310
+ dst = uglifyjs(ctx, dst, finaldst)
311
+ if force:
312
+ dst = copy_to_version(ctx, dst, force=force)
313
+
314
+ return dst
315
+
316
+
317
+ ns = Collection(browserify, uglifyjs)
318
+ ns.configure({
319
+ 'static': 'static/{pkg.name}'
320
+ })
dktasklib/lessc.py ADDED
@@ -0,0 +1,111 @@
1
+ import os
2
+ from dktasklib.wintask import task
3
+ from .rule import BuildRule
4
+ from .commands import Command
5
+ from dkfileutils.changed import Directory
6
+ from dkfileutils.path import Path
7
+ from invoke import Collection
8
+ from . import urlinliner
9
+ from .concat import copy
10
+ from .environment import env
11
+ from .utils import fmt, switch_extension, message
12
+ from .version import get_version
13
+ from .upversion import UpdateTemplateVersion
14
+
15
+ lessc = Command('lessc', '{opts} {src} {dst}',
16
+ requirements=('nodejs', 'npm', 'lessc'))
17
+
18
+
19
+ BOOTSTRAP = Path(os.environ.get('SRV', '')) / 'ext' / 'bootstrap' / 'less'
20
+
21
+
22
+ @task(
23
+ default=True,
24
+ name='build_less',
25
+ help={
26
+ 'version': "one of pkg|hash|svn",
27
+ }
28
+ )
29
+ class LessRule(BuildRule):
30
+ """Build a ``.less`` file into a versioned and minified ``.css`` file.
31
+ """
32
+
33
+ bootstrap_src = BOOTSTRAP
34
+
35
+ def __init__(self, *args, **kw):
36
+ self.after = [UpdateTemplateVersion(
37
+ kw.pop('import_fname',
38
+ '{pkg.source}/templates/{pkg.name}/{pkg.name}-css.html')
39
+ )]
40
+ super(LessRule.body, self).__init__(*args, **kw)
41
+
42
+ def __call__(self,
43
+ src='{pkg.source}/less/{pkg.name}.less',
44
+ dst='{pkg.source}/static/{pkg.name}/css/{pkg.name}-{version}.min.css',
45
+ version='pkg',
46
+ bootstrap=True,
47
+ force=False,
48
+ **kw):
49
+ c = env(self.ctx)
50
+ source = Path(fmt(src, c))
51
+ dest = Path(fmt(dst, c))
52
+
53
+ if not source.exists():
54
+ print("Missing source:", source, '(skipping)')
55
+ return
56
+
57
+ for fname in source.dirname().glob("*.inline"):
58
+ urlinliner.inline(self.ctx, fname)
59
+
60
+ if not force and not Directory(source.dirname()).changed(glob='**/*.less'):
61
+ print("No changes: {input_dir}/{glob}, add --force to build.".format(
62
+ input_dir=source.dirname(), glob='**/*.less'))
63
+ return
64
+
65
+ path = kw.pop('path', [])
66
+ if bootstrap:
67
+ path.append(self.bootstrap_src)
68
+
69
+ cssname = dest.relpath().format(version=get_version(self.ctx, source, version))
70
+ lessc(
71
+ self.ctx,
72
+ src=source.relpath(),
73
+ dst=cssname,
74
+ include_path=path,
75
+ strict_imports=True,
76
+ inline_urls=False,
77
+ autoprefix="last 4 versions",
78
+ clean_css="-b --s0",
79
+ )
80
+
81
+ with message("Creating copy without version number.."):
82
+ copy( # create a copy without version number too..
83
+ self.ctx,
84
+ cssname,
85
+ Path(cssname).dirname() / switch_extension(source.basename(), '.css'),
86
+ force=True
87
+ )
88
+ return cssname
89
+
90
+
91
+ ns = Collection('lessc', LessRule)
92
+ ns.configure({
93
+ 'force': False,
94
+ 'pkg': {
95
+ 'root': '<package-root-directory>',
96
+ 'name': '<package-name>',
97
+ 'source': '<source-dir>',
98
+ 'version': '<version-string>',
99
+ },
100
+ 'bootstrap': {
101
+ 'src': os.path.join(os.environ.get('BOOTSTRAPSRC', ''), 'less'),
102
+ },
103
+ 'lessc': {
104
+ 'use_bootstrap': False,
105
+ 'build_dir': 'build/css',
106
+ 'input_dir': '{pkg.source}/less',
107
+ 'input_fname': '{pkg.name}.less',
108
+ 'output_dir': '{pkg.source}/static/{pkg.name}/css/',
109
+ 'output_fname': '{pkg.name}.css',
110
+ }
111
+ })
dktasklib/manage.py ADDED
@@ -0,0 +1,71 @@
1
+ import sys
2
+ import os
3
+
4
+ from dkfileutils.path import Path
5
+ from dkfileutils.pfind import pfind
6
+ from dkfileutils.changed import changed, Directory
7
+ from dktasklib.wintask import task
8
+ from invoke import run
9
+ from .utils import cd, env, find_pymodule
10
+ from .package import Package
11
+
12
+
13
+ DEFAULT_SETTINGS_MODULE = 'settings'
14
+
15
+
16
+ @task
17
+ def manage(ctx, cmd, settings=None, manage_path=None, venv=None):
18
+ """Run manage.py with `settings` in a separate process.
19
+ """
20
+ settings = settings or DEFAULT_SETTINGS_MODULE
21
+ with env(DJANGO_SETTINGS_MODULE=settings, PYTHONWARNINGS='ignore'):
22
+ if manage_path is None:
23
+ settings_dir = find_pymodule(settings)
24
+ manage_path = Path(pfind(settings_dir, 'manage.py')).dirname()
25
+
26
+ with cd(manage_path):
27
+ call = "python manage.py {cmd}"
28
+ if venv:
29
+ call = "vex {venv} python manage.py {cmd} --traceback"
30
+
31
+ run(call.format(venv=venv, cmd=cmd))
32
+
33
+
34
+ @task
35
+ def collectstatic(ctx, settings=None, venv=None, clobber=False, force=False):
36
+ "Run collectstatic with settings from package.json ('django_settings_module')"
37
+ if not hasattr(ctx, 'pkg'):
38
+ ctx.pkg = Package()
39
+
40
+ if not (force or Directory(ctx.pkg.django_static).changed()):
41
+ print("Skipping collectstic: no changes to static dir.")
42
+ return
43
+
44
+ if not clobber:
45
+ # check that we don't overwrite versioned resources
46
+ changed_versioned_resources = False
47
+ static = Path(os.environ['SRV']) / 'data' / 'static'
48
+ for fname in ctx.pkg.django_static.glob(r'**/*\d+.min.*'):
49
+ pubname = static / fname.relpath(ctx.pkg.django_static)
50
+ if pubname.exists():
51
+ if pubname.open('rb').read() != fname.open('rb').read():
52
+ changed_versioned_resources = True
53
+ print()
54
+ print("ERROR: versioned file has changes:")
55
+ print(" contents of: ", fname)
56
+ print(" is different from:", pubname)
57
+ print()
58
+ if changed_versioned_resources:
59
+ print("Exiting due to changes in versioned resources. "
60
+ "You should probably revert the changes and create "
61
+ "a new version.")
62
+ sys.exit(1)
63
+
64
+ try:
65
+ settings = settings or ctx.pkg.django_settings_module
66
+ except AttributeError:
67
+ settings = DEFAULT_SETTINGS_MODULE
68
+ print("using settings:", settings, 'venv:', venv)
69
+ manage(ctx, "collectstatic --noinput", settings=settings, venv=venv)
70
+ # record changes made by collectstatic
71
+ changed(ctx.pkg.django_static)
dktasklib/npm.py ADDED
@@ -0,0 +1,19 @@
1
+ import subprocess
2
+
3
+ from dktasklib.executables import exe
4
+
5
+
6
+ def npm(cmdline):
7
+ npm_exe = exe.find('npm', requires=['nodejs']) # noqa
8
+ return subprocess.check_output("npm " + cmdline, shell=True).decode('u8')
9
+
10
+
11
+ def global_package(pkgname):
12
+ """Check if an npm package is installed globally.
13
+ """
14
+ try:
15
+ # this is the 'correct' way, but it's increadably slow (4+ secs)
16
+ npm('ls -g --depth 0 ' + pkgname)
17
+ return True
18
+ except subprocess.CalledProcessError:
19
+ return False
@@ -0,0 +1,31 @@
1
+ from dktasklib.wintask import task
2
+ from .package_interface import Package
3
+ from ..utils import format_table, Column
4
+
5
+
6
+ @task
7
+ def package(ctx):
8
+ """Print detected package directories.
9
+ """
10
+ pkg = ctx.pkg if hasattr(ctx, 'pkg') else Package()
11
+
12
+ def row(attribute, value, description=''):
13
+ return dict(attribute=attribute, value=value, description=description)
14
+
15
+ print("The dk-tasklib Package object thinks your code has the following layout:")
16
+
17
+ print(format_table(
18
+ [
19
+ row('package_name', pkg.package_name, '(repo/pip-installable name)'),
20
+ row('name', pkg.name, '(importable name)'),
21
+ row('root', pkg.root, '(wc)'),
22
+ row('source', pkg.source, '(source directory)'),
23
+ row('source_js', pkg.source_js, '(js source directory)'),
24
+ row('source_less', pkg.source_less, '(less source directory)'),
25
+ row('docs', pkg.docs, '(docs directory)'),
26
+ row('django?', 'yes' if pkg.is_django() else 'no', '(django?)'),
27
+ ],
28
+ Column(field='attribute'),
29
+ Column(field='value'),
30
+ Column(field='description'),
31
+ ))
@@ -0,0 +1,103 @@
1
+ import json
2
+ from configparser import RawConfigParser
3
+
4
+ import invoke
5
+ from dkfileutils.pfind import pfind as _pfind
6
+ from dkfileutils.path import Path
7
+ # from dkfileutils.changed import Directory
8
+ from invoke.config import Config
9
+ from dkpkg import Package as DKPKGPackage
10
+
11
+
12
+ def pfind(path, *fnames):
13
+ res = _pfind(path, *fnames)
14
+ return Path(res) if res is not None else None
15
+
16
+
17
+ class Package(DKPKGPackage):
18
+ overridables = DKPKGPackage.KEYS | {'version'}
19
+
20
+ def overrides(self, **res):
21
+ setup_py = pfind('.', 'setup.py')
22
+ if setup_py:
23
+ root = setup_py.dirname()
24
+ with root.abspath().cd():
25
+ res['version'] = self.ctx.run(
26
+ 'python setup.py --version',
27
+ hide=True
28
+ ).stdout.strip()
29
+
30
+ res.update(self._read_dkbuild_ini())
31
+
32
+ package_json = pfind('.', 'package.json')
33
+ if package_json:
34
+ with open(package_json, 'r') as fp:
35
+ pj = json.load(fp)
36
+ for k, v in pj.items():
37
+ if k in Package.overridables:
38
+ res[k] = v
39
+
40
+ return res
41
+
42
+ def _read_dkbuild_ini(self):
43
+ dkbuild_ini = pfind('.', 'dkbuild.ini')
44
+ if not dkbuild_ini:
45
+ return {}
46
+ cp = RawConfigParser()
47
+ cp.read(dkbuild_ini)
48
+ return dict((k, v) for k, v in cp.items('dkbuild')
49
+ if k in Package.overridables)
50
+
51
+ def __init__(self, ctx=None):
52
+ self.ctx = ctx or invoke.Context()
53
+ pkgdir = pfind('.',
54
+ 'setup.py',
55
+ 'dkbuild.ini',
56
+ 'package.json')
57
+ if pkgdir is None:
58
+ raise IOError("Didn't find setup.py|dkbuild.ini|package.json in "
59
+ "any parent directory up to, and including root.")
60
+ root = pkgdir.dirname()
61
+ ispkg = 'setup.py' in root
62
+ overrides = self.overrides() if ispkg else self.overrides(source=root)
63
+ super().__init__(root, **overrides)
64
+
65
+ def vcs(self):
66
+ """Return the name of the version control system handling this package.
67
+ """
68
+ if (self.root / '.svn').exists():
69
+ return 'svn'
70
+ elif (self.root / '.git').exists():
71
+ return 'git'
72
+ elif (self.root / '.hg').exists():
73
+ return 'hg'
74
+ else:
75
+ return ''
76
+
77
+ # invoke'ism?
78
+ def config(self): # pragma: nocover
79
+ cfg = Config(dict(iter(self)))
80
+ cfg.name = self.name
81
+ cfg.root = self.root
82
+ cfg.source = self.source
83
+ cfg.docs = self.docs
84
+ cfg.django_static = self.django_static
85
+ return cfg
86
+
87
+ def __repr__(self):
88
+ return self.__class__.__name__
89
+
90
+ def __getitem__(self, key):
91
+ try:
92
+ return getattr(self, key)
93
+ except AttributeError as e:
94
+ raise KeyError(str(e))
95
+
96
+ def get(self, key, default=None):
97
+ try:
98
+ return self[key]
99
+ except KeyError:
100
+ return default
101
+
102
+ def __iter__(self):
103
+ return iter([])