xdev package¶
Subpackages¶
- xdev.cli package
- Submodules
- Module contents
AvailablePackageConfig
DirectoryStatsCLI
ExtendedStubGenerator
PythonVersions
ReqPythonVersionSpec
XdevCLI
XdevCLI.InfoCLI
XdevCLI.CodeblockCLI
XdevCLI.SedCLI
XdevCLI.FindCLI
XdevCLI.TreeCLI
XdevCLI.PintCLI
XdevCLI.PyfileCLI
XdevCLI.PyVersionCLI
XdevCLI.EditfileCLI
XdevCLI.FormatQuotesCLI
XdevCLI.FreshPyenvCLI
XdevCLI.DocstrStubgenCLI
XdevCLI.AvailablePackageCLI
XdevCLI.DirectoryStatsCLI
XdevCLI.RegexCLI
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
InteractiveShellEmbedEnhanced
InteractiveShellEmbedEnhanced.user_global_ns
InteractiveShellEmbedEnhanced.init_frame()
InteractiveShellEmbedEnhanced._to_global()
InteractiveShellEmbedEnhanced._to_local()
InteractiveShellEmbedEnhanced.share_locals()
InteractiveShellEmbedEnhanced._all_trait_default_generators
InteractiveShellEmbedEnhanced._descriptors
InteractiveShellEmbedEnhanced._instance_inits
InteractiveShellEmbedEnhanced._static_immutable_initial_values
InteractiveShellEmbedEnhanced._trait_default_generators
InteractiveShellEmbedEnhanced._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
RegexBuilder
RegexBuilder.common_patterns
RegexBuilder.lookahead()
RegexBuilder.lookbehind()
RegexBuilder.named_field()
RegexBuilder.bref_field()
RegexBuilder.escape()
RegexBuilder.optional()
RegexBuilder.group()
RegexBuilder.oneof()
RegexBuilder.coerce()
RegexBuilder.identifier
RegexBuilder.hex
RegexBuilder.word
RegexBuilder.whitespace
RegexBuilder.nongreedy
RegexBuilder.number
VimRegexBuilder
PythonRegexBuilder
- 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.ChDir(dpath)[source]¶
Bases:
object
Context 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.EmbedOnException(before_embed=None)[source]¶
Bases:
object
Context manager which embeds in ipython if an exception is thrown
- SeeAlso:
- class xdev.GrepResult(fpath, pattern=None)[source]¶
Bases:
NiceRepr
Manage 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:
object
Choose 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
,NiceRepr
Example
>>> 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
,NiceRepr
Provides 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
fnmatch
module [fnmatch_docs]. The regex backend uses the Pythonre
module. The strict backend uses the “==” string equality testing. The parse backend uses theparse
module.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:
RegexBuilder
Contains 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.RegexBuilder[source]¶
Bases:
object
Notes
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 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.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:
RegexBuilder
https://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'\{-}'
- 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.repr2(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.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.difftext(text1, text2, context_lines=0, ignore_whitespace=False, colored=False)[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
- 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 >>> 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) >>> # verify results >>> print(result)
- 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
VISUAL
environment 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)[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.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
dirblocklist
to 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.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.
- 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.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.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.
- 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.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')[source]¶
Return sizes about set overlaps
- Parameters:
set1 (Iterable)
set2 (Iterable)
s1 (str) – name for set1
s2 (str) – name for set2
- Returns:
sizes of sets intersections unions and differences
- Return type:
Notes
- This function needs a rename. Possible candidates brainstorm:
set_analysis
set_binary_analysis
set_binary_describe
set_relationships
describe_sets
describe_relations
describe_set_relations
sets_summary
sumarize_sets
sumerset
- 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.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.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 │ │ └─╼ ... │ └─╼ ... └─╼ ...
- 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, **kwargs)[source]¶
Bases:
object
Configurable 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
**kwargs – passed to label options
- property file_paths¶
- property dir_paths¶