xdev package
Subpackages
- xdev.cli package
- Submodules
- Module contents
AvailablePackageConfigDirectoryStatsCLIExtendedStubGeneratorPythonVersionsReqPythonVersionSpecXdevCLIXdevCLI.InfoCLIXdevCLI.CodeblockCLIXdevCLI.SedCLIXdevCLI.FindCLIXdevCLI.TreeCLIXdevCLI.PintCLIXdevCLI.PyfileCLIXdevCLI.PyVersionCLIXdevCLI.EditfileCLIXdevCLI.FormatQuotesCLIXdevCLI.FreshPyenvCLIXdevCLI.DocstrStubgenCLIXdevCLI.AvailablePackageCLIXdevCLI.DirectoryStatsCLIXdevCLI.RegexCLIXdevCLI.CLIFormatterCLIXdevCLI.expand_import_star
build_package_table()common_module_aliases()common_module_names()common_unreferenced()cp_sorter()delete_unpaired_pyi_files()demo()generate_typed_stubs()grab_pypi_items()hacked_typing_info()minimum_cross_python_versions()modpath_coerce()parse_platform_tag()parse_wheel_name()postprocess_hacks()remove_duplicate_imports()rprint()stdlib_names()summarize_package_availability()vectorize()
Submodules
- xdev.__main__ module
- xdev._ipython_ext module
InteractiveShellEmbedEnhancedInteractiveShellEmbedEnhanced.user_global_nsInteractiveShellEmbedEnhanced.init_frame()InteractiveShellEmbedEnhanced._to_global()InteractiveShellEmbedEnhanced._to_local()InteractiveShellEmbedEnhanced.share_locals()InteractiveShellEmbedEnhanced._all_trait_default_generatorsInteractiveShellEmbedEnhanced._descriptorsInteractiveShellEmbedEnhanced._instance_initsInteractiveShellEmbedEnhanced._static_immutable_initial_valuesInteractiveShellEmbedEnhanced._trait_default_generatorsInteractiveShellEmbedEnhanced._traits
embed2()
- xdev.algo module
- xdev.autojit module
- xdev.class_reloader module
- xdev.desktop_interaction module
- xdev.embeding module
- xdev.format_quotes module
- xdev.interactive_iter module
- xdev.introspect module
- xdev.misc module
- xdev.patterns module
- xdev.profiler module
- xdev.regex_builder module
RegexBuilderRegexBuilder.common_patternsRegexBuilder.lookahead()RegexBuilder.lookbehind()RegexBuilder.named_field()RegexBuilder.bref_field()RegexBuilder.escape()RegexBuilder.optional()RegexBuilder.group()RegexBuilder.oneof()RegexBuilder.coerce()RegexBuilder.identifierRegexBuilder.hexRegexBuilder.wordRegexBuilder.whitespaceRegexBuilder.nongreedyRegexBuilder.integerRegexBuilder.number
VimRegexBuilderPythonRegexBuilder
- xdev.search_replace module
- xdev.tracebacks module
- xdev.util module
- xdev.util_networkx module
- xdev.util_path module
Module contents
This is Jon Crall’s xdev module.
These are tools I often use in IPython, but they almost never make it into
production code, otherwise they would be in ubelt.
Read the docs |
|
Github |
|
Pypi |
- class xdev.AsciiDirectedGlyphs[source]
Bases:
_AsciiBaseGlyphs- last = 'L-> '
- mid = '|-> '
- backedge = '<-'
- vertical_edge = 'v'
- class xdev.AsciiUndirectedGlyphs[source]
Bases:
_AsciiBaseGlyphs- last = 'L-- '
- mid = '|-- '
- backedge = '-'
- vertical_edge = '|'
- class xdev.AvailablePackageConfig(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigPrint a table of available versions of a python package on Pypi
Refactor of ~/local/tools/supported_python_versions_pip.py to report the available versions of a python package that meet some critera
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'package_name': <Value(None)>, 'refresh': <Value(False)>, 'request_min': <Value(None)>}
- class xdev.ChDir(dpath)[source]
Bases:
objectContext manager that changes the current working directory and then returns you to where you were.
- Parameters:
dpath (PathLike | None) – The new directory to work in. If None, then the context manager is disabled.
Example
>>> dpath = ub.Path.appdir('xdev/tests/chdir').ensuredir() >>> dir1 = (dpath / 'dir1').ensuredir() >>> dir2 = (dpath / 'dir2').ensuredir() >>> with ChDir(dpath): >>> assert ub.Path.cwd() == dpath >>> # changes to the given directory, and then returns back >>> with ChDir(dir1): >>> assert ub.Path.cwd() == dir1 >>> with ChDir(dir2): >>> assert ub.Path.cwd() == dir2 >>> # changes inside the context manager will be reset >>> os.chdir(dpath) >>> assert ub.Path.cwd() == dir1 >>> assert ub.Path.cwd() == dpath >>> with ChDir(dir1): >>> assert ub.Path.cwd() == dir1 >>> with ChDir(None): >>> assert ub.Path.cwd() == dir1 >>> # When disabled, the cwd does *not* reset at context exit >>> os.chdir(dir2) >>> assert ub.Path.cwd() == dir2 >>> os.chdir(dir1) >>> # Dont change dirs, but reset to your cwd at context end >>> with ChDir('.'): >>> os.chdir(dir2) >>> assert ub.Path.cwd() == dir1 >>> assert ub.Path.cwd() == dpath
- class xdev.DirectoryStatsCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigAnalysis for code in a repository
CommandLine
python ~/code/xdev/xdev/cli/repo_stats.py .
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'dpath': <Value('.')>, 'exclude_dnames': <Value(None)>, 'exclude_fnames': <Value(None)>, 'ignore_dotprefix': <Value(True)>, 'include_dnames': <Value(None)>, 'include_fnames': <Value(None)>, 'max_display_depth': <Value(None)>, 'max_files': <Value(None)>, 'max_walk_depth': <Value(None)>, 'parse_content': <Value(False)>, 'python': <Value(False)>, 'rust': <Value(False)>, 'verbose': <Value(0)>, 'version': <Value(False)>}
- main(**kwargs)
Example
>>> # xdoctest: +SKIP >>> cmdline = 0 >>> kwargs = dict(dpath='module:watch') >>> main(cmdline=cmdline, **kwargs)
- normalize()
- class xdev.DirectoryWalker(dpath, exclude_dnames=None, exclude_fnames=None, include_dnames=None, include_fnames=None, max_walk_depth=None, max_files=None, parse_content=False, show_progress=True, ignore_empty_dirs=False, sort=False, fs=None, **kwargs)[source]
Bases:
objectConfigurable directory walker that can explore a directory and report information about its contents in a concise manner.
Options will impact how long this process takes based on how much data / metadata we need to parse out of the filesystem.
- Parameters:
dpath (str | PathLike) – the path to walk
exclude_dnames (Coercable[MultiPattern]) – blocks directory names matching this pattern
exclude_fnames (Coercable[MultiPattern]) – blocks file names matching this pattern
include_dnames (Coercable[MultiPattern]) – if specified, excludes directories that do NOT match this pattern.
include_fnames (Coercable[MultiPattern]) – if specified, excludes files that do NOT match this pattern.
max_files (None | int) – ignore all files in directories with more than this number.
max_walk_depth (None | int) – how far to recurse
parse_content (bool) – if True, include content analysis
sort (bool) – if True, sort files and directories before adding them to the graph.
fs (fsspec.spec.AbstractFileSystem) – experimental: an fsspec filesystem
**kwargs – passed to label options
- property root
Alias for
self.dpath
- write_report(max_nodes=10, **nxtxt_kwargs)[source]
- Parameters:
**nxtxt_kwargs –
- pathstring or file or callable or None
Filename or file handle for data output. if a function, then it will be called for each generated line. if None, this will default to “sys.stdout.write”
- with_labelsbool | str
If True will use the “label” attribute of a node to display if it exists otherwise it will use the node value itself. If given as a string, then that attribute name will be used instead of “label”. Defaults to True.
- sourcesList
Specifies which nodes to start traversal from. Note: nodes that are not reachable from one of these sources may not be shown. If unspecified, the minimal set of nodes needed to reach all others will be used.
- max_depthint | None
The maximum depth to traverse before stopping. Defaults to None.
- ascii_onlyBoolean
If True only ASCII characters are used to construct the visualization
- endstring
The line ending character
- vertical_chainsBoolean
If True, chains of nodes will be drawn vertically when possible.
Example
>>> # xdoctest: +REQUIRES(module:pandas) >>> import xdev >>> walker = xdev.DirectoryWalker.demo() >>> walker.write_report(max_nodes=0)
- stats(typed=False, root=None)[source]
Return stats about the directories starting at the root. Requires walker has been built. If root unspecified uses walker root
- property file_paths
- property dir_paths
- _parallel_process_files(func, desc=None, max_workers=8, mode='thread')[source]
Applies a function to every node.
- classmethod demo()[source]
Create a persistent demo directory tree and return a built walker.
The directory is created under
ub.Path.appdir('directory_walker/demo')and is re-initialized on each call to keep doctests deterministic.- Returns:
DirectoryWalker
Example
>>> import xdev >>> walker = xdev.DirectoryWalker.demo() >>> walker.dpath.exists() True
- find(pattern, data=False, root=None, filetype=None)[source]
Search for nodes whose name matches a MultiPattern, optionally filtering by type.
- Parameters:
pattern – Coerced via
MultiPattern.coerce(pattern)and tested withpattern.match(node.name).data (bool) – if True, also yield the node data dict
root (pathlib.Path | None) – if specified, search descendants of this node
filetype (Iterable[str] | None) –
- Iterable of type chars from {‘f’, ‘d’, ‘l’}:
‘f’ = regular file
‘d’ = directory
‘l’ = symlink
Examples: ‘f’, ‘fd’, {‘l’}, [‘f’,’l’].
- Yields:
pathlib.Path | Tuple[pathlib.Path, dict]
Example
>>> import xdev >>> walker = DirectoryWalker.demo() >>> sorted(p.name for p in walker.find('foo.txt')) ['foo.txt'] >>> sorted(p.name for p in walker.find('foo*', filetype='f')) ['foo.md', 'foo.txt'] >>> sorted(p.name for p in walker.find('adir', filetype='d')) ['adir'] >>> # Best-effort: only assert something meaningful if the symlink exists >>> links = list(walker.find('alink.txt', filetype='l')) >>> (len(links) == 1) True
- find_one(pattern, data=False, root=None, filetype=None)[source]
Find exactly one node matching a pattern (and optional type filter).
- Parameters:
pattern – Coerced via
kwutil.MultiPattern.coerce(pattern)(seefind).data (bool) – if True, also return the node data dict.
root (pathlib.Path | None) – if specified, search descendants of this node.
filetype (Iterable[str] | None) – iterable of {‘f’,’d’,’l’} (see
find).
- Returns:
pathlib.Path | Tuple[pathlib.Path, dict]
- Raises:
KeyError – if zero or multiple matches are found.
Example
>>> walker = DirectoryWalker.demo() >>> walker.find_one('foo.txt').name 'foo.txt'
- class xdev.EmbedOnException(before_embed=None)[source]
Bases:
objectContext manager which embeds in ipython if an exception is thrown
- SeeAlso:
- class xdev.GrepResult(fpath, pattern=None)[source]
Bases:
NiceReprManage and format results from grep
- class xdev.InteractiveIter(iterable=None, enabled=True, startx=0, default_action='next', custom_actions=[], wraparound=False, display_item=False, verbose=True)[source]
Bases:
objectChoose next value interactively
iterable should be a list, not a generator. sorry
CommandLine
xdoctest -m xdev.interactive_iter InteractiveIter:0 --interact xdoctest -m xdev.interactive_iter InteractiveIter:1 --interact
Example
>>> # xdoctest: +REQUIRES(--interact) >>> from xdev.interactive_iter import * # NOQA >>> iterable = [1, 2, 3] >>> enabled = True >>> startx = 0 >>> default_action = 'next' >>> custom_actions = [] >>> wraparound = False >>> display_item = True >>> verbose = True >>> iiter = InteractiveIter(iterable, enabled, startx, default_action, custom_actions, wraparound, display_item, verbose) >>> for _ in iiter: >>> pass
Example
>>> # xdoctest: +REQUIRES(--interact) >>> # Interactive matplotlib stuff >>> from xdev.interactive_iter import * # NOQA >>> import kwimage >>> import kwplot >>> plt = kwplot.autoplt(verbose=3, force='Qt5Agg') >>> plt.ion() >>> keys = list(kwimage.grab_test_image.keys()) >>> iterable = [kwimage.grab_test_image(key) for key in keys] >>> iiter = InteractiveIter(iterable) >>> for img in iiter: >>> kwplot.imshow(img) >>> InteractiveIter.draw()
- Parameters:
iterable (None) – (default = None)
enabled (bool) – (default = True)
startx (int) – (default = 0)
default_action (str) – (default = ‘next’)
custom_actions (list) – list of 4-tuple (name, actions, help, func) (default = [])
wraparound (bool) – (default = False)
display_item (bool) – (default = True)
verbose (bool) – verbosity flag(default = True)
- class xdev.MultiPattern(patterns, predicate)[source]
Bases:
PatternBase,NiceReprExample
>>> dpath = ub.Path.appdir('xdev/tests/multipattern_paths').ensuredir().delete().ensuredir() >>> (dpath / 'file0.txt').touch() >>> (dpath / 'data0.dat').touch() >>> (dpath / 'other0.txt').touch() >>> ((dpath / 'dir1').ensuredir() / 'file1.txt').touch() >>> ((dpath / 'dir2').ensuredir() / 'file2.txt').touch() >>> ((dpath / 'dir2').ensuredir() / 'file3.txt').touch() >>> ((dpath / 'dir1').ensuredir() / 'data.dat').touch() >>> ((dpath / 'dir2').ensuredir() / 'data.dat').touch() >>> ((dpath / 'dir2').ensuredir() / 'data.dat').touch() >>> pat = MultiPattern.coerce(['*.txt'], 'glob') >>> print(list(pat.paths(cwd=dpath))) >>> pat = MultiPattern.coerce(['*0*', '**/*.txt'], 'glob') >>> print(list(pat.paths(cwd=dpath, recursive=1))) >>> pat = MultiPattern.coerce(['*.txt', '**/*.txt', '**/*.dat'], 'glob') >>> print(list(pat.paths(cwd=dpath)))
- classmethod coerce(data, hint='auto', predicate='any')[source]
- Parameters:
data (str | List | Pattern | PathLike | MultiPattern)
hint (str) – can be ‘glob’, ‘regex’, ‘strict’ or ‘auto’. In ‘auto’ we will use ‘glob’ if the input is a string and ‘*’ is in the pattern, otherwise we will use strict. Pattern inputs keep their existing interpretation.
- Returns:
MultiPattern
Example
>>> pat = MultiPattern.coerce('foo*', 'glob') >>> pat2 = MultiPattern.coerce(pat, 'regex') >>> pat3 = MultiPattern.coerce([pat, pat], 'regex') >>> pat4 = MultiPattern.coerce([ub.Path('bar*'), pat], 'regex') >>> print('pat = {}'.format(ub.urepr(pat, nl=1))) >>> print('pat2 = {}'.format(ub.urepr(pat2, nl=1))) >>> print('pat3 = {!r}'.format(pat3)) >>> print('pat4 = {!r}'.format(pat4))
>>> pat00 = MultiPattern.coerce('foo', 'glob') >>> pat01 = MultiPattern.coerce('foo*', 'glob') >>> pat02 = MultiPattern.coerce('foo*', 'regex') >>> pat5 = MultiPattern.coerce(['foo', 'foo*', pat, pat00, pat01, pat02]) >>> print(f'pat5={pat5}')
Example
>>> # Test all acceptable input types >>> import itertools as it >>> str_pat = 'pattern*' >>> scalar_inputs = { >>> 'str': str_pat, >>> 'path': ub.Path(str_pat), >>> 'pat': Pattern.coerce(str_pat), >>> 'mpat': MultiPattern.coerce(str_pat) >>> } >>> # Test scalar input types >>> scalar_outputs = {} >>> for k, v in scalar_inputs.items(): >>> scalar_outputs[k] = MultiPattern.coerce(v) >>> print('scalar_outputs = {}'.format(ub.urepr(scalar_outputs, nl=1))) >>> # >>> # Test iterable input types >>> multi_outputs = [] >>> for v in it.combinations(scalar_inputs.values(), 2): >>> multi_outputs.append(MultiPattern.coerce(v)) >>> for v in it.combinations(scalar_inputs.values(), 3): >>> multi_outputs.append(MultiPattern.coerce(v)) >>> # Higher order nesting test >>> higher_order_output = MultiPattern.coerce(multi_outputs) >>> print('higher_order_output = {}'.format(ub.urepr(higher_order_output, nl=1)))
- class xdev.Pattern(pattern, backend)[source]
Bases:
PatternBase,NiceReprProvides a common API to several common pattern matching syntaxes.
A general patterns class, which can use a backend from BACKENDS
- Parameters:
pattern (str | object) – The pattern text or a precompiled backend pattern object
backend (str) – Code indicating what backend the pattern text should be interpereted with. See BACKENDS for available choices.
Notes
# BACKENDS
The glob backend uses the
fnmatchmodule [fnmatch_docs]. The regex backend uses the Pythonremodule. The strict backend uses the “==” string equality testing. The parse backend uses theparsemodule.References
Example
>>> # Test Regex backend >>> repat = Pattern.coerce('foo.*', 'regex') >>> assert repat.match('foobar') >>> assert not repat.match('barfoo') >>> match = repat.search('baz-biz-foobar') >>> match = repat.match('baz-biz-foobar') >>> # Test Glob backend >>> globpat = Pattern.coerce('foo*', 'glob') >>> assert globpat.match('foobar') >>> assert not globpat.match('barfoo') >>> globpat = Pattern.coerce('[foo|bar]', 'glob') >>> globpat.match('foo')
Example
>>> # xdoctest: +REQUIRES(module:parse) >>> # Test parse backend >>> pattern1 = Pattern.coerce('A {adjective} pattern', 'parse') >>> result1 = pattern1.match('A cool pattern') >>> print(f'result1.named = {ub.urepr(result1.named, nl=1)}') >>> pattern2 = pattern1.to_regex() >>> result2 = pattern2.match('A cool pattern')
- to_regex()[source]
Returns an equivalent pattern with the regular expression backend
- Returns:
Pattern
Example
>>> globpat = Pattern.coerce('foo*', 'glob') >>> strictpat = Pattern.coerce('foo*', 'strict') >>> repat1 = strictpat.to_regex() >>> repat2 = globpat.to_regex() >>> print(f'repat1={repat1}') >>> print(f'repat2={repat2}')
- classmethod from_regex(data, flags=0, multiline=False, dotall=False, ignorecase=False)[source]
Create a Pattern object with a regex backend.
- classmethod coerce_backend(data, hint='auto')[source]
Example
>>> assert Pattern.coerce_backend('foo', hint='auto') == 'strict' >>> assert Pattern.coerce_backend('foo*', hint='auto') == 'glob' >>> assert Pattern.coerce_backend(re.compile('foo*'), hint='auto') == 'regex'
- classmethod coerce(data, hint='auto')[source]
Attempt to automatically determine the input data as the appropriate pattern. If it cannot be determined, then fallback to the hint.
- Parameters:
data (str | Pattern | PathLike)
hint (str) – can be ‘glob’, ‘regex’, ‘strict’ or ‘auto’. In ‘auto’ we will use ‘glob’ if the input is a string and ‘*’ is in the pattern, otherwise we will use strict. Pattern inputs keep their existing interpretation.
Example
>>> pat = Pattern.coerce('foo*', 'glob') >>> pat2 = Pattern.coerce(pat, 'regex') >>> print('pat = {}'.format(ub.urepr(pat, nl=1))) >>> print('pat2 = {}'.format(ub.urepr(pat2, nl=1)))
- class xdev.PythonRegexBuilder[source]
Bases:
RegexBuilderContains helper methods to construct a regex
Example
>>> b = PythonRegexBuilder() >>> pat_text = b.lookbehind('_') + r'v\d+' + b.optional(b.lookahead('_')) >>> pat = re.compile(pat_text) >>> print(pat.search('_v321_').group()) v321 >>> print(pat.search('_v321').group()) v321 >>> print(pat.search('fdsfds_v321_fdsfsd').group()) v321 >>> print(pat.search('fdsfds_v321fdsfsd').group()) v321 >>> print(pat.search('fdsfdsv321fdsfsd')) None
Example
>>> # Test multiple negative lookbehind >>> b = PythonRegexBuilder() >>> suffix = 'foo' >>> neg_prefix1 = b.lookbehind('abc', positive=0) >>> neg_prefix2 = b.lookbehind('efg', positive=0) >>> pat1 = re.compile(neg_prefix1 + suffix) >>> pat2 = re.compile(neg_prefix2 + suffix) >>> patB = re.compile(neg_prefix1 + neg_prefix2 + suffix) >>> cases = ['abcfoo', 'efgfoo', 'hijfoo', 'foo'] >>> print([bool(pat1.search(c)) for c in cases]) >>> print([bool(pat2.search(c)) for c in cases]) >>> print([bool(patB.search(c)) for c in cases]) [False, True, True, True] [True, False, True, True] [False, False, True, True]
References
https://www.dataquest.io/blog/regex-cheatsheet/ https://docs.python.org/3/library/re.html#regular-expression-syntax
- python_patterns = [{'alias': ['nongreedy_kleene_star'], 'docs': 'non-greedily matches zero or more of the pattern to the left', 'key': 'nongreedy_zero_or_more', 'pattern': '*?'}, {'docs': 'The boundary at the start or end of a word', 'key': 'boundary', 'pattern': '\\b'}, {'key': 'non-boundary', 'pattern': '\\B'}, {'key': 'left-expr', 'pattern': '\\A'}, {'docs': 'Matches only at the end of the string', 'key': 'right-expr', 'pattern': '\\Z'}]
- previous(min=None, max=None, exact=None, greedy=True)[source]
Match the previous pattern some number of times.
- Parameters:
min (int | None) – minimum number of matches
max (int | None) – maximum number of matches
exact (int | None) – Specify exact number of matches. Mutex with minimum and max.
greedy (bool) – if True match as many as possible, otherwise match as few as possible
Example
>>> from xdev.regex_builder import * # NOQA >>> b = PythonRegexBuilder() >>> assert b.previous(exact=1) == '{1}' >>> assert b.previous(min=1, max=3) == '{1,3}' >>> assert b.previous(min=1, max=3, greedy=False) == '{1,3}?' >>> assert b.previous(max=3) == '{,3}' >>> assert b.previous(min=3) == '{3,}' >>> assert b.previous() == '*' >>> assert b.previous(greedy=False) == '*?'
Example
>>> from xdev.regex_builder import * # NOQA >>> b = PythonRegexBuilder() >>> assert re.compile('a' + b.previous(exact=2) + '$').match('aa') >>> assert not re.compile('a' + b.previous(exact=2) + '$').match('aaa') >>> assert not re.compile('a' + b.previous(exact=2) + '$').match('a')
- class xdev.PythonVersions[source]
Bases:
objectClass that contains information about different Python versions
- class xdev.RegexBuilder[source]
Bases:
objectNotes
The way to have multiple negative look aheads/behinds is to change them together SO12689046
References
Example
b = RegexBuilder.coerce(‘python’) import re pat = re.compile(‘[A-Z-]+’)
- common_patterns = [{'docs': 'An alphanumeric word, i.e. [a-zA-Z0-9_] (also matches unicode characters in Python)', 'key': 'word', 'pattern': '\\w'}, {'docs': 'Anything not a word', 'key': 'non-word', 'pattern': '\\W'}, {'docs': 'Any space character including: " " "\\t", "\\n", "\\r"', 'key': 'space', 'pattern': '\\s'}, {'docs': 'Any non-space character', 'key': 'non-space', 'pattern': '\\S'}, {'docs': 'any number 0-9', 'key': 'digit', 'pattern': '\\d'}, {'docs': 'any non-digit', 'key': 'digit', 'pattern': '\\D'}, {'alias': ['kleene_star'], 'docs': 'zero or more of the pattern to the left', 'key': 'zero_or_more', 'pattern': '*'}]
- lookahead(pat, positive=True, mode='positive')[source]
A lookahead pattern that can be positive or negative
looklook
- property identifier
A word, except it must start with a letter or underscore (not a number)
References
Example
>>> from xdev.regex_builder import * # NOQA >>> b = PythonRegexBuilder() >>> assert re.match(b.identifier, 'hello') >>> assert re.match(b.identifier, 'hello') >>> assert re.match(b.identifier, '𝛣_ello') >>> assert re.match(b.identifier, 'h_1e8llo') >>> assert not re.match(b.identifier, '1hello')
- property hex
A case-independent hex character
- property word
- property whitespace
- property nongreedy
- property integer
Pattern to match an integer.
Example
>>> from xdev.regex_builder import * # NOQA >>> b = PythonRegexBuilder() >>> pat = re.compile('^' + b.integer + '$') >>> assert pat.match('3') >>> assert pat.match('+3') >>> assert pat.match('-32')
- property number
Can match a generic floating point number
References
https://www.regular-expressions.info/floatingpoint.html
Example
>>> from xdev.regex_builder import * # NOQA >>> b = PythonRegexBuilder() >>> pat = re.compile('^' + b.number + '$') >>> assert pat.match('3.4') >>> assert pat.match('3.4e-1') >>> assert pat.match('3.4') >>> assert pat.match('3.4e+1') >>> assert not pat.match('3.4a+1')
>>> b = PythonRegexBuilder() >>> num_part = b.named_field(b.number, name='number') >>> space_part = b.named_field(' *', name='spaces') >>> unit_part = b.named_field('.*', name='unit') >>> pat = re.compile('^' + num_part + space_part + unit_part + '$') >>> pat.match('3.4').groupdict() >>> pat.match('3.1415 foobars').groupdict() >>> pat.match('3.1415foobars').groupdict() >>> pat.match('+3.1415e9foobars').groupdict()
- class xdev.ReqPythonVersionSpec(pattern)[source]
Bases:
objectFor python_version specs in requirements files
Example
>>> pattern = '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*' >>> other = '3.7.2' >>> reqspec = ReqPythonVersionSpec(pattern) >>> reqspec.highest_explicit() >>> reqspec.matches('2.6') >>> reqspec.matches('2.7')
Example
>>> self = ReqPythonVersionSpec('~=3.2') >>> self.highest_explicit()
- class xdev.UtfDirectedGlyphs[source]
Bases:
_UtfBaseGlyphs- last = '└─╼ '
- mid = '├─╼ '
- backedge = '╾'
- vertical_edge = '╽'
- class xdev.UtfUndirectedGlyphs[source]
Bases:
_UtfBaseGlyphs- last = '└── '
- mid = '├── '
- backedge = '─'
- vertical_edge = '│'
- class xdev.VimRegexBuilder[source]
Bases:
RegexBuilderhttps://dev.to/iggredible/learning-vim-regex-26ep
- vim_patterns = [{'alias': ['nongreedy_kleene_star'], 'docs': 'non-greedily matches zero or more of the pattern to the left', 'key': 'nongreedy_zero_or_more', 'pattern': '\\{-}'}]
- previous(min=None, max=None, exact=None, greedy=True)[source]
Match the previous pattern some number of times.
- Parameters:
min (int | None) – minimum number of matches
max (int | None) – maximum number of matches
exact (int | None) – Specify exact number of matches. Mutex with minimum and max.
greedy (bool) – if True match as many as possible, otherwise match as few as possible
Example
>>> from xdev.regex_builder import * # NOQA >>> b = VimRegexBuilder() >>> assert b.previous(exact=1) == r'\{1}' >>> assert b.previous(min=1, max=3) == r'\{1,3}' >>> assert b.previous(min=1, max=3, greedy=False) == r'\{-1,3}' >>> assert b.previous(max=3) == r'\{,3}' >>> assert b.previous(min=3) == r'\{3,}' >>> assert b.previous() == '*' >>> assert b.previous(greedy=False) == r'\{-}'
- class xdev.XdevCLI(description: str = '', sub_clis: List[Dict[str, Any]] | None = None, version: str | None = None)[source]
Bases:
ModalCLIThe XDEV CLI
A collection of excellent developer tools for excellent developers.
- class InfoCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigInfo about xdev
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {}
- class CodeblockCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigRemove indentation from text.
Useful for writing subscripts (e.g. python -c code) in shell files without having to resort to ugly indentation.
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- classmethod main(cmdline=False, **kwargs)[source]
Example
>>> from xdev.cli.main import * # NOQA >>> CodeblockCLI.main(cmdline=0, text='foobar')
- default = {'text': <Value('')>}
- class SedCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigSearch and replace text in files
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'dirblocklist': <Value(None)>, 'dpath': <Value(None)>, 'dry': <Value('ask')>, 'exclude': <Value(None)>, 'include': <Value(None)>, 'recursive': <Value(True)>, 'regexpr': <Value('')>, 'repl': <Value('')>, 'verbose': <Value(2)>}
- class FindCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigFind matching files or paths in a directory.
This is similar to the GNU find program, but written in Python. Important differences are that this program is:
has pattern first argument and uses the cwd by default.
recursive by default
has explicit include / exclude options
Example
xdev find “*.py”
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'dirblocklist': <Value(None)>, 'dpath': <Value(None)>, 'exclude': <Value(None)>, 'followlinks': <Value(False)>, 'include': <Value(None)>, 'pattern': <Value('')>, 'recursive': <Value(True)>, 'type': <Value('f')>}
- class TreeCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigList a directory like a tree
See also
The,xdevExample
xdev tree .
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'colors': <Value(False)>, 'cwd': <Value('.')>, 'dirblocklist': <Value(None)>, 'ignore_dotprefix': <Value(True)>, 'max_depth': <Value(None)>, 'max_files': <Value(100)>}
- class PintCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigConverts one type of unit to another via the pint library.
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'input_expr': <Value(None)>, 'output_unit': <Value(None)>, 'precision': <Value(2)>}
- class PyfileCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigPrints the path corresponding to a Python module.
This uses the
ubelt.modname_to_modpathmechanism that does not require importing of your package.Alternatives
An alternative with no dependencies is to use the one-liner:
python -c “import <modname>; print(<modname>.__file__)”
Example Usage
xdev pyfile xdev xdev pyfile numpy
# Use this feature in scripts for developement to avoid referencing # machine-specific paths. MODPATH=$(xdev pyfile ubelt) echo “MODPATH = $MODPATH”
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'modname': <Value(None)>}
- class PyVersionCLI(*args: Any, **kwargs: Any)
Bases:
DataConfigDetect and print the version of a Python module or package.
Note
Different backends may produce different results, especially for packages that are in development and were installed in development mode.
An alternative with no dependencies is to use the one-liner:
python -c “import <modname>; print(<modname>.__version__)”
xdev pyversion xdev xdev pyversion numpy
# Both the module name and the package name can be used. xdev pyversion cv2 xdev pyversion opencv-python-headless
# For more verbose information add the verbose flag xdev pyversion opencv-python-headless –verbose
xdev pyversion xdev –backend=import xdev pyversion xdev –backend=importlib
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'backend': <Value('auto')>, 'modname': <Value(None)>, 'verbose': <Value(False)>}
- classmethod main(cmdline=False, **kwargs)
- class EditfileCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigOpens a file in your visual editor determined by the
VISUALenvironment variable.If
VISUALis unspecified it attempts to default to the first known existing editor.Example Usage
xdev edit xdev xdev edit numpy
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'target': <Value(None)>}
- class FormatQuotesCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigUse single quotes for code and double quotes for docs.
This is useful for “fixing” quotations after running a code formater like black on a module.
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'diff': <Value(True)>, 'path': <Value('')>, 'recursive': <Value(True)>, 'verbose': <Value(3)>, 'write': <Value(False)>}
- class FreshPyenvCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigCreate a fresh environment in a docker container to test a Python package.
SeeAlso
The generic freshpyenv.sh bash script also installed with this package.
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'image': <Value('__default__')>}
- class DocstrStubgenCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigGenerate Typed Stubs from Docstrings (experimental)
Note
This is an experimental command and currently requires a specialized patch to mypy to work correctly.
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'module': <Value(None)>}
- class AvailablePackageCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigPrint a table of available versions of a python package on Pypi
Refactor of ~/local/tools/supported_python_versions_pip.py to report the available versions of a python package that meet some critera
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'package_name': <Value(None)>, 'refresh': <Value(False)>, 'request_min': <Value(None)>}
- class DirectoryStatsCLI(*args: Any, **kwargs: Any)
Bases:
DataConfigAnalysis for code in a repository
CommandLine
python ~/code/xdev/xdev/cli/repo_stats.py .
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- classmethod _register_main(func)
- default = {'dpath': <Value('.')>, 'exclude_dnames': <Value(None)>, 'exclude_fnames': <Value(None)>, 'ignore_dotprefix': <Value(True)>, 'include_dnames': <Value(None)>, 'include_fnames': <Value(None)>, 'max_display_depth': <Value(None)>, 'max_files': <Value(None)>, 'max_walk_depth': <Value(None)>, 'parse_content': <Value(False)>, 'python': <Value(False)>, 'rust': <Value(False)>, 'verbose': <Value(0)>, 'version': <Value(False)>}
- main(**kwargs)
Example
>>> # xdoctest: +SKIP >>> cmdline = 0 >>> kwargs = dict(dpath='module:watch') >>> main(cmdline=cmdline, **kwargs)
- normalize()
- class RegexCLI(*args: Any, **kwargs: Any)[source]
Bases:
DataConfigQuery the regex builder for help on the command line. By default prints useful regex constructs I have a hard time remembering.
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'backend': <Value('python')>}
- class CLIFormatterCLI(*args: Any, **kwargs: Any)
Bases:
DataConfigThe idea is that we can ingest a dictionary, argv list, or a command line string and convert between any of these formats.
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'input': <Value(None)>, 'input_type': <Value('auto')>, 'output_type': <Value('all')>}
- classmethod main(cmdline=1, **kwargs)
Example
>>> # xdoctest: +SKIP >>> from cli_formatter import * # NOQA >>> cmdline = 0 >>> kwargs = dict() >>> cls = CLIFormatterCLI >>> cls.main(cmdline=cmdline, **kwargs)
- expand_import_star
alias of
ExpandImportStarCLI
- xdev.bubbletext(text, font='cybermedium')[source]
Uses pyfiglet to create bubble text.
- Parameters:
font (str) – default=cybermedium, other fonts include: cybersmall and cyberlarge.
References
Example
>>> bubble_text = bubbletext('TESTING BUBBLE TEXT', font='cybermedium') >>> print(bubble_text)
- xdev.byte_str(num, unit='auto', precision=2)[source]
Automatically chooses relevant unit (KB, MB, or GB) for displaying some number of bytes.
- Parameters:
num (int) – number of bytes
unit (str) – which unit to use, can be auto, B, KB, MB, GB, TB, PB, EB, ZB, or YB.
precision (int) – number of decimals of precision
References
https://en.wikipedia.org/wiki/Orders_of_magnitude_(data)
- Returns:
string representing the number of bytes with appropriate units
- Return type:
Example
>>> num_list = [1, 100, 1024, 1048576, 1073741824, 1099511627776] >>> result = ub.urepr(list(map(byte_str, num_list)), nl=0) >>> print(result) ['0.00 KB', '0.10 KB', '1.00 KB', '1.00 MB', '1.00 GB', '1.00 TB']
- xdev.common_module_names()[source]
fpath = ub.grabdata(’https://raw.githubusercontent.com/hugovk/top-pypi-packages/main/top-pypi-packages.json’, expires=86400) fpath = ub.Path(fpath) import json data = json.loads(fpath.read_text()) for item in data[‘rows’][0:300]:
pkg_name = item[‘project’] if ‘-’ not in pkg_name:
print(f’{pkg_name!r},’)
- xdev.conj_phrase(list_, cond='or')[source]
Joins a list of words using English conjunction rules
- Parameters:
list_ (list) – of strings
cond (str) – a conjunction (or, and, but)
- Returns:
the joined cconjunction phrase
- Return type:
References
http://en.wikipedia.org/wiki/Conjunction_(grammar)
Example
>>> list_ = ['a', 'b', 'c'] >>> result = conj_phrase(list_, 'or') >>> print(result) a, b, or c
- Example1:
>>> list_ = ['a', 'b'] >>> result = conj_phrase(list_, 'and') >>> print(result) a and b
- xdev.delete_unpaired_pyi_files(modpath)[source]
Cleanup pyi files corresponding to renamed or removed py files.
- xdev.difftext(text1, text2, context_lines=0, ignore_whitespace=False, colored=False, style='ndiff', fromfile='', tofile='')[source]
Uses difflib to return a difference string between two similar texts
- Parameters:
text1 (str) – old text
text2 (str) – new text
context_lines (int) – number of lines of unchanged context
ignore_whitespace (bool)
colored (bool) – if true highlight the diff
style (str) – can be ndiff or unified (git style)
fromfile (str) – unified diff “old” header label
tofile (str) – unified diff “new” header label
- Returns:
formatted difference text message
- Return type:
References
http://www.java2s.com/Code/Python/Utility/IntelligentdiffbetweentextfilesTimPeters.htm
Example
>>> # build test data >>> text1 = 'one\ntwo\nthree' >>> text2 = 'one\ntwo\nfive' >>> # execute function >>> result = difftext(text1, text2) >>> # verify results >>> print(result) - three + five
Example
>>> # build test data >>> from xdev.misc import * # NOQA >>> text1 = 'one\ntwo\nthree\n3.1\n3.14\n3.1415\npi\n3.4\n3.5\n4' >>> text2 = 'one\ntwo\nfive\n3.1\n3.14\n3.1415\npi\n3.4\n4' >>> # execute function >>> context_lines = 1 >>> result = difftext(text1, text2, context_lines, colored=True) >>> print(result) >>> # >>> result = difftext(text1, text2, context_lines, colored=True, style='unified') >>> print(result)
Example
>>> # build test data for a git-apply-able unified patch >>> from xdev.misc import * # NOQA >>> text1 = 'alpha\nbeta\ngamma\n' >>> text2 = 'alpha\nbeta\nGAMMA\ndelta\n' >>> patch = difftext(text1, text2, context_lines=3, style='unified', colored=True, ... fromfile='a/example.txt', tofile='b/example.txt') >>> print(patch) >>> lines = patch.splitlines() >>> assert lines[0] == '--- a/example.txt' >>> assert lines[1] == '+++ b/example.txt' >>> assert lines[2].startswith('@@')
- xdev.distext(obj)[source]
Like dis.dis, but returns the text
- Parameters:
obj – a compiled object (e.g. a function, class, generator, etc…)
- Returns:
text of the python byte code
- Return type:
Example
>>> from xdev.introspect import * # NOQA >>> import ubelt as ub >>> code = ub.codeblock( ''' def foo(a, b): print('hello') if __debug__: if a is None or b is None: raise ValueError print('world') # This is not entirely optimized away if __debug__ and (a is None or b is None): raise ValueError return a + b ''') >>> obj = compile(code, filename='<memory>', mode='exec', dont_inherit=True, optimize=0) >>> print(' ** UNOPTIMIZED') >>> text = distext(obj) >>> print(text) >>> print(' ** OPTIMIZED') >>> obj = compile(code, filename='<memory>', mode='exec', dont_inherit=True, optimize=1) >>> text = distext(obj) >>> print(text)
- xdev.edit_distance(string1, string2)[source]
Edit distance algorithm. String1 and string2 can be either strings or lists of strings
- Parameters:
string1 (str | List[str])
string2 (str | List[str])
- Requirements:
pip install python-Levenshtein
- Returns:
float | List[float] | List[List[float]]
Example
>>> # xdoctest: +REQUIRES(module:Levenshtein) >>> string1 = 'hello world' >>> string2 = ['goodbye world', 'rofl', 'hello', 'world', 'lowo'] >>> edit_distance(['hello', 'one'], ['goodbye', 'two']) >>> edit_distance('hello', ['goodbye', 'two']) >>> edit_distance(['hello', 'one'], 'goodbye') >>> edit_distance('hello', 'goodbye') >>> distmat = edit_distance(string1, string2) >>> result = ('distmat = %s' % (ub.repr2(distmat),)) >>> print(result) >>> [7, 9, 6, 6, 7]
- xdev.editfile(fpath, verbose=True)[source]
Opens a file or code corresponding to a live python object in your preferred visual editor. This function is mainly useful in an interactive IPython session.
The visual editor is determined by the
VISUALenvironment variable. If this is not specified it defaults to gvim.- Parameters:
fpath (PathLike | ModuleType | str) – a file path or python module / function. If the input is a string it will interpret it either as a Path or a module name. Ambiguity is resolved by choosing a path if the string resolves to an existing path, and then checking if the string corresponds to a module name.
verbose (int) – verbosity
Example
>>> # xdoctest: +SKIP >>> # This test interacts with a GUI frontend, not sure how to test. >>> import xdev >>> ub.editfile(xdev.misc.__file__) >>> ub.editfile(xdev) >>> ub.editfile(xdev.editfile)
- xdev.embed(parent_locals=None, parent_globals=None, exec_lines=None, remove_pyqt_hook=True, n=0, debug=False)[source]
Starts interactive session. Similar to keyboard command in matlab. Wrapper around IPython.embed.
Note
Contains helper logic to allow the developer to more easilly exit the program if embed is called in a loop. Specifically if you want to quit and not embed again, then set qqq=1 before exiting the embed session.
- SeeAlso:
embed_on_exception()
- xdev.embed_if_requested(n=0)[source]
Calls xdev.embed conditionally based on the environment.
Useful in cases where you want to leave the embed call around, but you dont want it to trigger in normal circumstances.
Specifically, embed is only called if the environment variable XDEV_EMBED exists or if –xdev-embed is in sys.argv.
- xdev.ensure_rng(rng=None, api='python')[source]
Coerces input into a random number generator.
This function is useful for ensuring that your code uses a controlled internal random state that is independent of other modules.
If the input is None, then a global random state is returned.
If the input is a numeric value, then that is used as a seed to construct a random state.
If the input is a random number generator, then another random number generator with the same state is returned. Depending on the api, this random state is either return as-is, or used to construct an equivalent random state with the requested api.
- Parameters:
rng (int | float | None | numpy.random.RandomState | random.Random) – if None, then defaults to the global rng. Otherwise this can be an integer or a RandomState class. Defaults to the global random.
api (str) – specify the type of random number generator to use. This can either be ‘numpy’ for a
numpy.random.RandomStateobject or ‘python’ for arandom.Randomobject. Defaults to numpy.
- Returns:
rng - either a numpy or python random number generator, depending on the setting of
api.- Return type:
Example
>>> # xdoctest: +REQUIRES(module:numpy) >>> from xdev.util_random import * # NOQA >>> from xdev.util_random import ensure_rng >>> rng = ensure_rng(None) >>> ensure_rng(0, 'python').randint(0, 1000) 864 >>> # xdoctest: +REQUIRES(module:numpy) >>> import numpy as np >>> ensure_rng(np.random.RandomState(1)).randint(0, 1000) 427
Example
>>> from xdev.util_random import * # NOQA >>> from xdev.util_random import ensure_rng >>> num = 4 >>> print('--- Python as PYTHON ---') >>> py_rng = random.Random(0) >>> pp_nums = [py_rng.random() for _ in range(num)] >>> print(pp_nums) >>> print('--- Numpy as PYTHON ---') >>> # xdoctest: +REQUIRES(module:numpy) >>> import numpy as np >>> np_rng = ensure_rng(random.Random(0), api='numpy') >>> np_nums = [np_rng.rand() for _ in range(num)] >>> print(np_nums) >>> print('--- Numpy as NUMPY---') >>> np_rng = np.random.RandomState(seed=0) >>> nn_nums = [np_rng.rand() for _ in range(num)] >>> print(nn_nums) >>> print('--- Python as NUMPY---') >>> py_rng = ensure_rng(np.random.RandomState(seed=0), api='python') >>> pn_nums = [py_rng.random() for _ in range(num)] >>> print(pn_nums) >>> assert np_nums == pp_nums >>> assert pn_nums == nn_nums
Example
>>> # Test that random modules can be coerced >>> # xdoctest: +REQUIRES(module:numpy) >>> from xdev.util_random import * # NOQA >>> import random >>> import numpy as np >>> ensure_rng(random, api='python') >>> ensure_rng(random, api='numpy') >>> ensure_rng(np.random, api='python') >>> ensure_rng(np.random, api='numpy')
- xdev.find(pattern=None, dpath=None, include=None, exclude=None, dirblocklist=None, type=None, recursive=True, followlinks=False)[source]
Find all paths in a root subject to a search criterion
- Parameters:
pattern (str | Pattern | None) – The glob pattern the path name must match to be returned
dpath (str | Pattern | None) – The root directory to search. Can also be a filepath, in which case, that is the only filepath considered. NOTE: in the future, this argument may change to path to indicate specifying a filepath is allowed. Defaults to cwd.
include (str | List[str] | MultiPattern | None) – Pattern or list of patterns. If specified, search only files whose base name matches this pattern. By default the pattern is GLOB. This only applies to the final name. Directories that do not match this name will still be traversed.
exclude (str | List[str] | MultiPattern | None) – Pattern or list of patterns. Skip any file with a name suffix that matches the pattern. By default the pattern is GLOB. This ONLY applies to the final name. Directories that match an exclude pattern will still be traversed. Use
dirblocklistto specify patterns to exclude intermediate directories from traversal.dirblocklist (str | List[str] | MultiPattern | None) – Any directory name matching this pattern will be removed from traversal.
type (str | List[str] | None) – A list of 1 character codes indicating what types of file can be returned. Currently we only allow either “f” for file or “d” for directory. Symbolic links are not currently distinguished. In the future we may support posix codes, see [1]_ for details.
recursive – search all subdirectories recursively
followlinks (bool, default=False) – if True will follow directory symlinks
References
_[1] https://linuxconfig.org/identifying-file-types-in-linux
Todo
mindepth
maxdepth
ignore_case
regex_match
Example
>>> from xdev.search_replace import * # NOQA >>> from xdev.search_replace import _create_test_filesystem >>> dpath = _create_test_filesystem()['root'] >>> paths = list(find(pattern='*', dpath=dpath)) >>> assert len(paths) == 5 >>> paths = list(find(pattern='*', dpath=dpath, type='f')) >>> assert len(paths) == 4
- xdev.fix_embed_globals()[source]
HACK adds current locals() to globals(). Can be dangerous.
References
https://github.com/ipython/ipython/issues/62
Solves the following issue:
def foo(): x = 5 # You embed here import xdev xdev.embed() ''' Now you try and run this line manually but you get a NameError result = [x + i for i in range(10)] No problem, just use. It changes all local variables to globals xdev.fix_embed_globals() ''' result = [x + i for i in range(10)] foo()
- xdev.format_quotes_in_file(fpath, diff=True, write=False, verbose=3)[source]
Autoformat quotation marks in Python files
- Parameters:
fpath (str) – The file to format
diff (bool) – if True write the diff between old and new to stdout
write (bool) – if True write the modifications to disk
verbose (int) – verbosity level
- xdev.format_quotes_in_text(text, backend='parso')[source]
Reformat text according to formatting rules
- Parameters:
text (str) – python source code
- Returns:
modified text
- Return type:
Example
>>> # xdoctest: +REQUIRES(module:parso) >>> from xdev.format_quotes import * # NOQA >>> text = ub.codeblock( ... f''' ... def func1(): ... {TRIPLE_SINGLE_QUOTE} ... Fix this. ... {TRIPLE_SINGLE_QUOTE} ... ... def func2(): ... {TRIPLE_DOUBLE_QUOTE} ... Leave the doctests alone! ... {TRIPLE_DOUBLE_QUOTE} ... ... string1 = "fix these" ... string2 = "don't fix these" ... string3 = {TRIPLE_SINGLE_QUOTE}this is ok{TRIPLE_SINGLE_QUOTE} ... string4 = {TRIPLE_DOUBLE_QUOTE}fix this{TRIPLE_DOUBLE_QUOTE} ... ... def func3(): ... inside_string1 = "fix these" ... inside_string2 = "don't fix these" ... inside_string3 = {TRIPLE_SINGLE_QUOTE}this is ok{TRIPLE_SINGLE_QUOTE} ... inside_string4 = {TRIPLE_DOUBLE_QUOTE}fix this{TRIPLE_DOUBLE_QUOTE} ... ''') >>> print(text) >>> fixed = format_quotes_in_text(text) >>> print(fixed) >>> import xdev >>> fixed = format_quotes_in_text(text, backend='parso') >>> print('----') >>> print(xdev.difftext(text, fixed, colored=True)) >>> # xdoctest: +REQUIRES(module:redbaron) >>> print('----') >>> fixed = format_quotes_in_text(text, backend='redbaron') >>> print('----') >>> print(xdev.difftext(text, fixed, colored=True)) >>> print('----')
- xdev.generate_network_text(graph, with_labels=True, sources=None, max_depth=None, ascii_only=False, vertical_chains=False)[source]
Generate lines in the “network text” format
This works via a depth-first traversal of the graph and writing a line for each unique node encountered. Non-tree edges are written to the right of each node, and connection to a non-tree edge is indicated with an ellipsis. This representation works best when the input graph is a forest, but any graph can be represented.
This notation is original to networkx, although it is simple enough that it may be known in existing literature. See #5602 for details. The procedure is summarized as follows:
1. Given a set of source nodes (which can be specified, or automatically discovered via finding the (strongly) connected components and choosing one node with minimum degree from each), we traverse the graph in depth first order.
Each reachable node will be printed exactly once on it’s own line.
Edges are indicated in one of three ways:
a. a parent “L-style” connection on the upper left. This corresponds to a traversal in the directed DFS tree.
b. a backref “<-style” connection shown directly on the right. For directed graphs, these are drawn for any incoming edges to a node that is not a parent edge. For undirected graphs, these are drawn for only the non-parent edges that have already been represented (The edges that have not been represented will be handled in the recursive case).
c. a child “L-style” connection on the lower right. Drawing of the children are handled recursively.
4. The children of each node (wrt the directed DFS tree) are drawn underneath and to the right of it. In the case that a child node has already been drawn the connection is replaced with an ellipsis (”…”) to indicate that there is one or more connections represented elsewhere.
5. If a maximum depth is specified, an edge to nodes past this maximum depth will be represented by an ellipsis.
- Parameters:
graph (nx.DiGraph | nx.Graph) – Graph to represent
with_labels (bool | str) – If True will use the “label” attribute of a node to display if it exists otherwise it will use the node value itself. If given as a string, then that attribte name will be used instead of “label”. Defaults to True.
sources (List) – Specifies which nodes to start traversal from. Note: nodes that are not reachable from one of these sources may not be shown. If unspecified, the minimal set of nodes needed to reach all others will be used.
max_depth (int | None) – The maximum depth to traverse before stopping. Defaults to None.
ascii_only (Boolean) – If True only ASCII characters are used to construct the visualization
vertical_chains (Boolean) – If True, chains of nodes will be drawn vertically when possible.
- Yields:
str (a line of generated text)
Example – >>> # xdoctest: +REQUIRES(module:networkx) >>> graph = nx.path_graph(10) >>> graph.add_node(‘A’) >>> graph.add_node(‘B’) >>> graph.add_node(‘C’) >>> graph.add_node(‘D’) >>> graph.add_edge(9, ‘A’) >>> graph.add_edge(9, ‘B’) >>> graph.add_edge(9, ‘C’) >>> graph.add_edge(‘C’, ‘D’) >>> graph.add_edge(‘C’, ‘E’) >>> graph.add_edge(‘C’, ‘F’) >>> write_network_text(graph) ╙── 0
- └── 1
- └── 2
- └── 3
- └── 4
- └── 5
- └── 6
- └── 7
- └── 8
- └── 9
├── A ├── B └── C
├── D ├── E └── F
>>> write_network_text(graph, vertical_chains=True) ╙── 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 ├── A ├── B └── C ├── D ├── E └── F
- xdev.generate_typed_stubs(modpath)[source]
Attempt to use google-style docstrings, xdoctest, and mypy to generate typed stub files.
Does not overwrite anything by itself.
- Parameters:
modpath (PathLike) – path to the module to generate types for
- Returns:
A dictionary mapping the path of each file to write to the text to be written.
- Return type:
Dict[PathLike, str]
Notes
FIXME: This currently requires my hacked version of mypy
CommandLine
xdoctest -m /home/joncrall/code/xdev/xdev/cli/docstr_stubgen.py generate_typed_stubs --hacked
Example
>>> # xdoctest: +REQUIRES(module:mypy) >>> # xdoctest: +REQUIRES(--hacked) >>> from xdev.cli.docstr_stubgen import * # NOQA >>> import xdev >>> import ubelt as ub >>> from xdev.cli import docstr_stubgen >>> modpath = ub.Path(docstr_stubgen.__file__) >>> generated = generate_typed_stubs(modpath) >>> text = generated[ub.peek(generated.keys())] >>> assert 'PathLike' in text >>> assert 'Dict' in text >>> print(text)
- xdev.get_func_kwargs(func, max_depth=None)[source]
Dynamically parse the kwargs accepted by this function.
This function uses Python signatures where possible, but it also uses heuristics by inspecting the way any keywords dictionary is used.
- Parameters:
func (callable) – function to introspect kwargs from
max_depth (int, default=None) – by default we recursively parse any kwargs passed to subfunctions.
Example
>>> from xinspect.dynamic_kwargs import get_func_kwargs >>> parsed_kwargs = get_func_kwargs(get_func_kwargs) >>> assert parsed_kwargs == {'max_depth': None}
- xdev.get_stack_frame(N=0, strict=True)[source]
- Parameters:
N (int) – N=0 means the frame you called this function in. N=1 is the parent frame.
strict (bool) – (default = True)
- xdev.grab_pypi_items(package_name, refresh=False)[source]
Get all the information about a package from pypi
- xdev.graph_str(graph, with_labels=True, sources=None, write=None, ascii_only=False)[source]
Creates a nice utf8 representation of a forest
This function has been superseded by
nx.readwrite.text.generate_network_text(), which should be used instead.- Parameters:
graph (nx.DiGraph | nx.Graph) – Graph to represent (must be a tree, forest, or the empty graph)
with_labels (bool) – If True will use the “label” attribute of a node to display if it exists otherwise it will use the node value itself. Defaults to True.
sources (List) – Mainly relevant for undirected forests, specifies which nodes to list first. If unspecified the root nodes of each tree will be used for directed forests; for undirected forests this defaults to the nodes with the smallest degree.
write (callable) – Function to use to write to, if None new lines are appended to a list and returned. If set to the print function, lines will be written to stdout as they are generated. If specified, this function will return None. Defaults to None.
ascii_only (Boolean) – If True only ASCII characters are used to construct the visualization
- Returns:
utf8 representation of the tree / forest
- Return type:
str | None
Example
>>> # xdoctest: +REQUIRES(module:networkx) >>> graph = nx.balanced_tree(r=2, h=3, create_using=nx.DiGraph) >>> print(graph_str(graph)) ╙── 0 ├─╼ 1 │ ├─╼ 3 │ │ ├─╼ 7 │ │ └─╼ 8 │ └─╼ 4 │ ├─╼ 9 │ └─╼ 10 └─╼ 2 ├─╼ 5 │ ├─╼ 11 │ └─╼ 12 └─╼ 6 ├─╼ 13 └─╼ 14
>>> # xdoctest: +REQUIRES(module:networkx) >>> graph = nx.balanced_tree(r=1, h=2, create_using=nx.Graph) >>> print(graph_str(graph)) ╙── 0 └── 1 └── 2
>>> # xdoctest: +REQUIRES(module:networkx) >>> print(graph_str(graph, ascii_only=True)) +-- 0 L-- 1 L-- 2
- xdev.grep(regexpr, dpath=None, include=None, exclude=None, recursive=True, dirblocklist=None, verbose=1)[source]
Execute a grep on multiple files.
- Parameters:
regexpr (str | Pattern) – pattern to find
dpath (str | None) – passed to
find().include (str | List[str] | MultiPattern | None) – passed to
find().exclude (str | List[str] | MultiPattern | None) – passed to
find().recursive (bool) – passed to
find().dirblocklist (str | List[str] | MultiPattern | None) – passed to
find().verbose (int) – verbosity level
- Return type:
List[GrepResult]
Example
>>> from xdev.search_replace import * # NOQA >>> from xdev.search_replace import _create_test_filesystem >>> dpath = _create_test_filesystem()['root'] >>> grep('a', dpath=dpath)
- xdev.grepfile(fpath, regexpr, verbose=1)[source]
Exceute grep on a single file
- Parameters:
fpath (str | PathLike) – file to search
regexpr (str | Pattern) – pattern to find
verbose (int) – verbosity level
- Returns:
None | GrepResult
Example
>>> from xdev.search_replace import * # NOQA >>> from xdev.search_replace import _create_test_filesystem >>> fpath = _create_test_filesystem()['contents'][1] >>> grep_result = grepfile(fpath, r'\bb\b') >>> print('grep_result = {}'.format(grep_result))
- xdev.greptext(text, regexpr, fpath=None, verbose=1)[source]
Exceute grep on text
- Parameters:
text (str) – text to search
regexpr (str | Pattern) – pattern to find
verbose (int) – verbosity level
- Returns:
None | GrepResult
- xdev.import_module_from_pyx(fname, dpath=None, error='raise', autojit=True, verbose=1, recompile=False, annotate=False)[source]
Attempts to import a module corresponding to a pyx file.
If the corresponding compiled module is not found, this can attempt to JIT-cythonize the pyx file.
- Parameters:
fname (str) – The basename of the cython pyx file
dpath (str) – The directory containing the cython pyx file
error (str) – Can be “raise” or “ignore”
autojit (bool) – If True, we will cythonize and compile the pyx file if possible.
verbose (int) – verbosity level (higher is more verbose)
recompile (bool) – if True force recompile
- Returns:
ModuleType | None – Returns the compiled and imported module if possible, otherwise None
- Return type:
module
- xdev.knapsack(items, maxweight, method='iterative')[source]
Solve the knapsack problem by finding the most valuable subsequence of items subject that weighs no more than maxweight.
- Parameters:
items (tuple) – is a sequence of tuples (value, weight, id_), where value is a number and weight is a non-negative integer, and id_ is an item identifier.
maxweight (numbers.Real) – a non-negative number indicating the maximum weight of items we can take.
- Returns:
- (total_value, items_subset) - a pair whose first element is the
sum of values in the most valuable subsequence, and whose second element is the subsequence. Subset may be different depending on implementation (ie top-odwn recusrive vs bottom-up iterative)
- Return type:
References
http://codereview.stackexchange.com/questions/20569/dynamic-programming-solution-to-knapsack-problem http://stackoverflow.com/questions/141779/solving-the-np-complete-problem-in-xkcd http://www.es.ele.tue.nl/education/5MC10/Solutions/knapsack.pdf
Example
>>> # ENABLE_DOCTEST >>> import ubelt as ub >>> items = [(4, 12, 0), (2, 1, 1), (6, 4, 2), (1, 1, 3), (2, 2, 4)] >>> maxweight = 15 >>> total_value1, items_subset1 = knapsack(items, maxweight, method='iterative') >>> print('items_subset1 = {}'.format(ub.repr2(items_subset1, nl=1))) >>> print('total_value1 = {}'.format(ub.repr2(total_value1, nl=1)))
Example
>>> # ENABLE_DOCTEST >>> # Solve https://xkcd.com/287/ >>> weights = [2.15, 2.75, 3.35, 3.55, 4.2, 5.8] * 2 >>> items = [(w, w, i) for i, w in enumerate(weights)] >>> maxweight = 15.05 >>> total_value1, items_subset1 = knapsack(items, maxweight, method='iterative') >>> total_weight = sum([t[1] for t in items_subset1]) >>> print('total_weight = %r' % (total_weight,)) >>> print('items_subset1 = %r' % (items_subset1,)) >>> #assert items_subset1 == items_subset, 'NOT EQ\n%r !=\n%r' % (items_subset1, items_subset)
- Timeit:
# >>> import ubelt as ub # >>> setup = ub.codeblock( # >>> ‘’’ # import ubelt as ut # weights = [215, 275, 335, 355, 42, 58] * 40 # items = [(w, w, i) for i, w in enumerate(weights)] # maxweight = 2505 # #import numba # #knapsack_numba = numba.autojit(knapsack_iterative) # #knapsack_numba = numba.autojit(knapsack_iterative_numpy) # ‘’’) # >>> # Test load time # >>> stmt_list1 = ub.codeblock( # >>> ‘’’ # knapsack_iterative(items, maxweight) # knapsack_ilp(items, maxweight) # #knapsack_numba(items, maxweight) # #knapsack_iterative_numpy(items, maxweight) # ‘’’).split(’n’)
# ut.util_dev.timeit_compare(stmt_list1, setup, int(5))
from xdev.algo import * # NOQA def knapsack_inputs(n):
rng = np.random items = [
(rng.randint(0, 10), rng.randint(0, 10), idx) for idx in range(n)]
return {‘items’: items, ‘maxweight’: 100}
- def input_wrapper(func):
import functools @functools.wraps(func) def _wrap(kwargs):
return func(**kwargs)
return _wrap
import perfplot perfplot.show(
setup=knapsack_inputs, kernels=list(map(input_wrapper, [knapsack_iterative, knapsack_ilp])), n_range=[2 ** k for k in range(15)], xlabel=”maxweight”, equality_check=None,
)
- xdev.knapsack_greedy(items, maxweight)[source]
non-optimal greedy version of knapsack algorithm does not sort input. Sort the input by largest value first if desired.
- Parameters:
`items` (tuple) – is a sequence of tuples (value, weight, id_), where value is a scalar and weight is a non-negative integer, and id_ is an item identifier.
`maxweight` (scalar) – is a non-negative integer.
Example
>>> # ENABLE_DOCTEST >>> items = [(4, 12, 0), (2, 1, 1), (6, 4, 2), (1, 1, 3), (2, 2, 4)] >>> maxweight = 15 >>> total_value, items_subset = knapsack_greedy(items, maxweight) >>> result = 'total_value = %r\n' % (total_value,) >>> result += 'items_subset = %r' % (items_subset,) >>> print(result) total_value = 7 items_subset = [(4, 12, 0), (2, 1, 1), (1, 1, 3)]
- xdev.knapsack_ilp(items, maxweight, verbose=False)[source]
solves knapsack using an integer linear program
Example
>>> # DISABLE_DOCTEST >>> import ubelt as ub >>> # Solve https://xkcd.com/287/ >>> weights = [2.15, 2.75, 3.35, 3.55, 4.2, 5.8, 6.55] >>> values = [2.15, 2.75, 3.35, 3.55, 4.2, 5.8, 6.55] >>> indices = ['mixed fruit', 'french fries', 'side salad', >>> 'hot wings', 'mozzarella sticks', 'sampler plate', >>> 'barbecue'] >>> items = [(v, w, i) for v, w, i in zip(values, weights, indices)] >>> #items += [(3.95, 3.95, 'mystery plate')] >>> maxweight = 15.05 >>> verbose = True >>> total_value, items_subset = knapsack_ilp(items, maxweight, verbose) >>> print('items_subset = %s' % (ub.repr2(items_subset, nl=1),))
- xdev.knapsack_iterative_int(items, maxweight)[source]
Iterative knapsack method
- Math:
maximize sum_{i in T} v_i subject to sum_{i in T} w_i leq W
Notes
dpmat is the dynamic programming memoization matrix. dpmat[i, w] is the total value of the items with weight at most W T is idx_subset, the set of indicies in the optimal solution
Example
>>> # ENABLE_DOCTEST >>> weights = [1, 3, 3, 5, 2, 1] * 2 >>> items = [(w, w, i) for i, w in enumerate(weights)] >>> maxweight = 10 >>> items = [(.8, 700, 0)] >>> maxweight = 2000 >>> print('maxweight = %r' % (maxweight,)) >>> print('items = %r' % (items,)) >>> total_value, items_subset = knapsack_iterative_int(items, maxweight) >>> total_weight = sum([t[1] for t in items_subset]) >>> print('total_weight = %r' % (total_weight,)) >>> print('items_subset = %r' % (items_subset,)) >>> result = 'total_value = %.2f' % (total_value,) >>> print(result) total_value = 0.80
- xdev.knapsack_iterative_numpy(items, maxweight)[source]
Iterative knapsack method
maximize sum_{i in T} v_i subject to sum_{i in T} w_i leq W
Notes
dpmat is the dynamic programming memoization matrix. dpmat[i, w] is the total value of the items with weight at most W T is the set of indicies in the optimal solution
- xdev.load_snapshot(fpath, parent_globals=None)[source]
Loads a snapshot of a local state from disk.
- Parameters:
fpath (str | PathLike) – the path to the snapshot file to load
parent_globals (dict | None) – The state dictionary to update. Should be given as
globals(). If unspecified, it is inferred via frame inspection.
- xdev.make_warnings_print_tracebacks()[source]
Makes warnings show tracebacks when displayed.
Applies a monkeypatch to warnings.formatwarning that includes a traceback in the displayed warning message.
Note
Use the context manager :class:WarningsWithTracebacks instead, or
WarningsWithTracebacks.apply()if global modification is needed.Example
>>> # xdoctest: +SKIP >>> make_warnings_print_tracebacks() >>> warnings.warn('This is a test warning')
- xdev.minimum_cross_python_versions(package_name, request_min=None, refresh=False)[source]
package_name = ‘scipy’ request_min = None
package_name = ‘opencv-python-headless’
package_name = ‘numpy’ request_min = ‘1.21.0’
- xdev.modpath_coerce(modpath_coercable)[source]
if modpath_coercable is a name, statically converts it to a path
- Parameters:
modpath_coercable (str | PathLike | ModuleType) – something we can extract a path to a module from.
- Returns:
the coerced modpath
- Return type:
Example
>>> # xdoctest: +SKIP >>> from xdev.cli.docstr_stubgen import * # NOQA >>> import xdev >>> modpath_coercable = xdev >>> modpath = modpath_coerce(modpath_coercable) >>> print(f'modpath={modpath}') >>> assert modpath_coerce(modpath) == modpath >>> assert modpath_coerce(xdev.__name__) == modpath
- xdev.nested_type(obj, unions=False)[source]
Compute the :module:`typing` compatible annotation type.
- Parameters:
obj (Any) – a typing template based on a specific object
unions (bool) – if True use unions, otherwise use Any
- Returns:
type code (might change to return actual type)
- Return type:
Example
>>> obj = {'a': [1, 2], 'b': [3, 4, 5]} >>> print(nested_type(obj)) Dict[str, List[int]]
>>> import numpy as np >>> obj = {'b': {'a': 1.0, 'b': 'foo', 'c': np.array([1, 2])}} >>> print(nested_type(obj, unions=True)) Dict[str, Dict[str, float | ndarray | str]]
- xdev.number_of_decimals(num)[source]
- Parameters:
num (float)
References
stackoverflow.com/questions/6189956/finding-decimal-places
Example
>>> # ENABLE_DOCTEST >>> num = 15.05 >>> result = number_of_decimals(num) >>> print(result) 2
- xdev.parse_file_stats(fpath, parse_content=True, fs=None)[source]
Get information about a file, including things like number of code lines / documentation lines, if that sort of information is available.
- xdev.parse_platform_tag(platform_tag)[source]
Parse finer grained information out of the package platform tag.
Example
>>> cases = [ >>> 'manylinux1_x86_64', >>> 'macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64', >>> 'manylinux1_i686', >>> 'win32', >>> 'win_amd64', >>> 'macosx_10_9_x86_64', >>> 'macosx_10_9_intel', >>> 'macosx_10_6_intel', >>> 'manylinux2010_i686', >>> 'manylinux2010_x86_64', >>> 'manylinux2014_aarch64', >>> 'manylinux_2_12_i686.manylinux2010_i686', >>> 'manylinux_2_12_x86_64.manylinux2010_x86_64', >>> 'manylinux_2_17_aarch64.manylinux2014_aarch64', >>> 'manylinux_2_5_i686.manylinux1_i686', >>> 'manylinux_2_5_x86_64.manylinux1_x86_64', >>> 'macosx_10_9_universal2', >>> 'macosx_11_0_arm64', >>> 'manylinux_2_17_x86_64.manylinux2014_x86_64', >>> 'macosx_10_14_x86_64', >>> 'macosx_10_15_x86_64', >>> 'macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64'] >>> for platform_tag in cases: >>> plat_info = parse_platform_tag(platform_tag) >>> print(f'platform_tag={platform_tag}') >>> print('plat_info = {}'.format(ub.repr2(plat_info, nl=1)))
- xdev.profile_now(func)[source]
Wrap a function to print profile information after it is called.
- Parameters:
func (Callable) – function to profile
- Returns:
the wrapped function
- Return type:
Callable
Example
>>> # xdoctest: +SKIP >>> from xdev.profiler import * # NOQA >>> def func_to_profile(): >>> list(range(10)) >>> tuple(range(100)) >>> set(range(1000)) >>> profile_now(func_to_profile)() # xdoctest: +IGNORE_WANT
Timer unit: 1e-09 s
Total time: 2.7767e-05 s File: <ipython-input-11-049a3440df03> Function: func_to_profile at line 3
Line # Hits Time Per Hit % Time Line Contents
3 def func_to_profile(): 4 1 3200.0 3200.0 11.5 list(range(10)) 5 1 1949.0 1949.0 7.0 tuple(range(100)) 6 1 22618.0 22618.0 81.5 set(range(1000))
- xdev.quantum_random(pure=False)[source]
Returns a quantum random number as a 32 bit unsigned integer. Does this by making a network request to the ANU Quantum Random Number Generator web service, so an internet connection is required.
- Parameters:
pure (bool) – if False, mixes this data with pseudorandom data for security. Otherwise returns the raw quantum numbers that were sent over the web (i.e. subject to MitM attacks).
- Requirements:
quantumrandom >= 1.9.0
- Returns:
the random number
- Return type:
numpy.uint32
- xdev.reload_class(self, verbose=True, reload_module=True)[source]
Reload a class for a specific instance.
(populates any new methods that may have just been written)
Ported from utool
- xdev.sed(regexpr, repl, dpath=None, include=None, exclude=None, dirblocklist=None, recursive=True, dry=False, verbose=1)[source]
Execute a sed on multiple files.
- Parameters:
regexpr (str | Pattern) – pattern to find
repl (str) – the text to replace the found pattern with
dpath (str | None) – passed to
find().include (str | List[str] | MultiPattern | None) – passed to
find().exclude (str | List[str] | MultiPattern | None) – passed to
find().dirblocklist (str | List[str] | MultiPattern | None) – passed to
find().recursive (bool) – passed to
find().dry (bool) – if True does not apply edits
verbose (int) – verbosity level
Example
>>> from xdev.search_replace import * # NOQA >>> from xdev.search_replace import _create_test_filesystem >>> dpath = _create_test_filesystem()['root'] >>> sed('a', 'x', dpath=dpath, dry=True)
- xdev.sedfile(fpath, regexpr, repl, dry=False, verbose=1)[source]
Execute a search and replace on a particular file
- Parameters:
fpath (str | PathLike) – file to search / replace on
regexpr (str | Pattern) – pattern to find
repl (str) – the text to replace the found pattern with
dry (bool) – if True does not apply edits
verbose (int) – verbosity level
- Returns:
changed lines
- Return type:
Todo
[ ] Store “SedResult” class, with lazy execution
Example
>>> from xdev.search_replace import * # NOQA >>> from xdev.search_replace import _create_test_filesystem >>> fpath = _create_test_filesystem()['contents'][1] >>> changed_lines1 = sedfile(fpath, 'a', 'x', dry=True, verbose=1) >>> changed_lines2 = sedfile(fpath, 'a', 'x', dry=False, verbose=0) >>> assert changed_lines2 == changed_lines1 >>> changed_lines3 = sedfile(fpath, 'a', 'x', dry=False, verbose=0) >>> assert changed_lines3 != changed_lines2
- xdev.set_overlaps(set1, set2, s1='s1', s2='s2', n_samples=None)[source]
Return sizes about set overlaps.
If the inputs are not sets, they will be cast to sets, and if there are duplicate they will be counted.
- Parameters:
set1 (Iterable) – the first set of items
set2 (Iterable) – the second set of items
s1 (str) – name for set1
s2 (str) – name for set2
n_samples (int | None) – provide up to n examples from each set.
- Returns:
sizes of sets intersections unions and differences
- Return type:
Example
>>> import ubelt as ub >>> from xdev.misc import set_overlaps >>> set1 = {'a', 'b', 'c', 'd', 'e'} >>> set2 = {'a', 'e', 'i', 'o', 'u'} >>> result = set_overlaps(set1, set2, 'first5', 'vowels') >>> print(f'result = {ub.urepr(result, nl=1)}') result = { 'first5': 5, 'vowels': 5, 'isect': 2, 'union': 8, 'first5 - vowels': 3, 'vowels - first5': 3, }
Example
>>> import ubelt as ub >>> from xdev.misc import set_overlaps >>> set1 = [1, 1, 2, 3, 3, 3, 4, 5,] >>> set2 = [2, 2, 2, 3] >>> result = set_overlaps(set1, set2) >>> print(f'result = {ub.urepr(result, nl=1)}')
- xdev.sidecar_glob(main_pat, sidecar_ext, main_key='main', sidecar_key=None, recursive=0)[source]
Similar to a regular glob, but returns a dictionary with associated main-file / sidecar-file pairs.
Todo
add as a general option to Pattern.paths?
- Parameters:
main_pat (str | PathLike) – glob pattern for the main non-sidecar file
- Yields:
Dict[str, ub.Path | None]
Notes
A sidecar file is defined by the sidecar extension. We usually use this for .dvc sidecars.
When the pattern includes a .dvc suffix, the result will include those .dvc files and any matching main files they correspond to. Note: if you search for paths like foo_*.dvc this might skiped unstaged files. Therefore it is recommended to only include the .dvc suffix in the pattern ONLY if you do not want any unstaged files.
If you want both staged and unstaged files, ensure the pattern does not exclude objects without a .dvc suffix (i.e. don’t end the pattern with .dvc).
When the pattern does not include a .dvc suffix, we include all those files, for other files that exist by adding a .dvc suffix.
With the pattern matches both a dvc and non-dvc file, they are grouped together.
Example
>>> from xdev.util_path import * # NOQA >>> dpath = ub.Path.appdir('xdev/tests/sidecar_glob') >>> dpath.delete().ensuredir() >>> (dpath / 'file1').touch() >>> (dpath / 'file1.ext').touch() >>> (dpath / 'file1.ext.car').touch() >>> (dpath / 'file2.ext').touch() >>> (dpath / 'file3.ext.car').touch() >>> (dpath / 'file4.car').touch() >>> (dpath / 'file5').touch() >>> (dpath / 'file6').touch() >>> (dpath / 'file6.car').touch() >>> (dpath / 'file7.bike').touch() >>> def _handle_results(results): ... results = list(results) ... for row in results: ... for k, v in row.items(): ... if v is not None: ... row[k] = v.relative_to(dpath) ... print(ub.repr2(results, sv=1)) ... return results >>> main_key = 'main', >>> sidecar_key = '.car' >>> sidecar_ext = '.car' >>> main_pat = dpath / '*' >>> _handle_results(sidecar_glob(main_pat, sidecar_ext)) >>> _handle_results(sidecar_glob(dpath / '*.ext', '.car')) >>> _handle_results(sidecar_glob(dpath / '*.car', '.car')) >>> _handle_results(sidecar_glob(dpath / 'file*.ext', '.car')) >>> _handle_results(sidecar_glob(dpath / '*', '.ext'))
- xdev.snapshot(parent_ns=None, n=0)[source]
Save a snapshot of the local state to disk.
Serialize all names in scope to a pickle and save to disk. Also print the command that will let the user start an IPython session with this namespace.
- Parameters:
parent_ns (dict) – A dictionary containing all of the available names in the scope to export.
n (int) – if
parent_nsis unspecified, infer locals and globals from the framenstack levels above the namespace this function is called in.
Todo
[ ] need to handle __main__
References
- xdev.startfile(fpath, verbose=True)[source]
Uses default program defined by the system to open a file. This is done via os.startfile on windows, open on mac, and xdg-open on linux.
- Parameters:
fpath (PathLike) – a file to open using the program associated with the files extension type.
verbose (int) – verbosity
References
http://stackoverflow.com/questions/2692873/quote-posix
Example
>>> # xdoctest: +SKIP >>> # This test interacts with a GUI frontend, not sure how to test. >>> import ubelt as ub >>> base = ub.ensure_app_cache_dir('ubelt') >>> fpath1 = join(base, 'test_open.txt') >>> ub.touch(fpath1) >>> proc = ub.startfile(fpath1)
- xdev.strip_comments_and_newlines(source)[source]
Removes hashtag comments from underlying source
- Parameters:
source (str | List[str])
Todo
would be better if this was some sort of configurable minify API
Example
>>> from xdev.directory_walker import strip_comments_and_newlines >>> import ubelt as ub >>> fmtkw = dict(sss=chr(39) * 3, ddd=chr(34) * 3) >>> source = ub.codeblock( >>> ''' # comment 1 a = '# not a comment' # comment 2
>>> non_comments = strip_comments_and_newlines(source) >>> print(non_comments) >>> assert non_comments.count(chr(10)) == 10 >>> assert non_comments.count('#') == 1
- xdev.strip_docstrings(tokens)[source]
Replace docstring tokens with NL tokens in a tokenize stream.
Any STRING token not part of an expression is deemed a docstring. Indented docstrings are not yet recognised.
- xdev.summarize_package_availability(package_name)[source]
Todo
for each released version of the package we want to know
- For source distros:
Does it need to be compiled?
What are is the min (or max?) python version
- For binaries:
What python version, arch, and os targets are available.
- xdev.tree(path)[source]
Like os.walk but yields a flat list of file and directory paths
- Parameters:
path (str | os.PathLike) – path to traverse
- Yields:
str – path
Example
>>> import itertools as it >>> import ubelt as ub >>> path = ub.Path('.') >>> gen = tree(path) >>> results = list(it.islice(gen, 5)) >>> print('results = {}'.format(ub.repr2(results, nl=1)))
- xdev.tree_repr(cwd=None, max_files=100, dirblocklist=None, show_nfiles='auto', return_text=False, return_tree=False, pathstyle='name', max_depth=None, with_type=False, abs_root_label=True, ignore_dotprefix=True, colors=False)[source]
Filesystem tree representation
Like the unix util tree, but allow writing numbers of files per directory when given -d option
- Parameters:
cwd (None | str | PathLike) – directory to print
max_files (int | None) – maximum files to print before supressing a directory
pathstyle (str) – can be rel, name, or abs
return_tree (bool) – if True return the tree
return_text (bool) – if True return the text
maxdepth (int | None) – maximum depth to descend
abs_root_label (bool) – if True force the root to always be absolute
colors (bool) – if True use rich
- SeeAlso:
xdev.tree - generator
- xdev.view_directory(dpath=None, verbose=False)[source]
View a directory in the operating system file browser. Currently supports windows explorer, mac open, and linux nautlius.
- Parameters:
dpath (PathLike | None) – directory name
verbose (bool) – verbosity
- xdev.write_network_text(graph, path=None, with_labels=True, sources=None, max_depth=None, ascii_only=False, end='\n', vertical_chains=False)[source]
Creates a nice text representation of a graph
This works via a depth-first traversal of the graph and writing a line for each unique node encountered. Non-tree edges are written to the right of each node, and connection to a non-tree edge is indicated with an ellipsis. This representation works best when the input graph is a forest, but any graph can be represented.
- Parameters:
graph (nx.DiGraph | nx.Graph) – Graph to represent
path (string or file or callable or None) – Filename or file handle for data output. if a function, then it will be called for each generated line. if None, this will default to “sys.stdout.write”
with_labels (bool | str) – If True will use the “label” attribute of a node to display if it exists otherwise it will use the node value itself. If given as a string, then that attribte name will be used instead of “label”. Defaults to True.
sources (List) – Specifies which nodes to start traversal from. Note: nodes that are not reachable from one of these sources may not be shown. If unspecified, the minimal set of nodes needed to reach all others will be used.
max_depth (int | None) – The maximum depth to traverse before stopping. Defaults to None.
ascii_only (Boolean) – If True only ASCII characters are used to construct the visualization
end (string) – The line ending characater
vertical_chains (Boolean) – If True, chains of nodes will be drawn vertically when possible.
Example
>>> # xdoctest: +REQUIRES(module:networkx) >>> graph = nx.balanced_tree(r=2, h=2, create_using=nx.DiGraph) >>> write_network_text(graph) ╙── 0 ├─╼ 1 │ ├─╼ 3 │ └─╼ 4 └─╼ 2 ├─╼ 5 └─╼ 6
>>> # A near tree with one non-tree edge >>> graph.add_edge(5, 1) >>> write_network_text(graph) ╙── 0 ├─╼ 1 ╾ 5 │ ├─╼ 3 │ └─╼ 4 └─╼ 2 ├─╼ 5 │ └─╼ ... └─╼ 6
>>> graph = nx.cycle_graph(5) >>> write_network_text(graph) ╙── 0 ├── 1 │ └── 2 │ └── 3 │ └── 4 ─ 0 └── ...
>>> graph = nx.cycle_graph(5, nx.DiGraph) >>> write_network_text(graph, vertical_chains=True) ╙── 0 ╾ 4 ╽ 1 ╽ 2 ╽ 3 ╽ 4 └─╼ ...
>>> write_network_text(graph, vertical_chains=True, ascii_only=True) +-- 0 <- 4 v 1 v 2 v 3 v 4 L-> ...
>>> graph = nx.generators.barbell_graph(4, 2) >>> write_network_text(graph, vertical_chains=False) ╙── 4 ├── 5 │ └── 6 │ ├── 7 │ │ ├── 8 ─ 6 │ │ │ └── 9 ─ 6, 7 │ │ └── ... │ └── ... └── 3 ├── 0 │ ├── 1 ─ 3 │ │ └── 2 ─ 0, 3 │ └── ... └── ... >>> write_network_text(graph, vertical_chains=True) ╙── 4 ├── 5 │ │ │ 6 │ ├── 7 │ │ ├── 8 ─ 6 │ │ │ │ │ │ │ 9 ─ 6, 7 │ │ └── ... │ └── ... └── 3 ├── 0 │ ├── 1 ─ 3 │ │ │ │ │ 2 ─ 0, 3 │ └── ... └── ...
>>> graph = nx.complete_graph(5, create_using=nx.Graph) >>> write_network_text(graph) ╙── 0 ├── 1 │ ├── 2 ─ 0 │ │ ├── 3 ─ 0, 1 │ │ │ └── 4 ─ 0, 1, 2 │ │ └── ... │ └── ... └── ...
>>> graph = nx.complete_graph(3, create_using=nx.DiGraph) >>> write_network_text(graph) ╙── 0 ╾ 1, 2 ├─╼ 1 ╾ 2 │ ├─╼ 2 ╾ 0 │ │ └─╼ ... │ └─╼ ... └─╼ ...