Package: cs.lex
Lexical analysis functions, tokenisers, transcribers:
an arbitrary assortment of lexical and tokenisation functions useful
for writing recursive descent parsers, of which I have several.
There are also some transcription functions for producing text
from various objects, such ashexifyandunctrl.Latest release 20240211:
New split_remote_path() function to recognise [[user@]host]:path.Generally the get_* functions accept a source string and an offset
(usually optional, default0) and return a token and the new offset,
raisingValueErroron failed tokenisation.Functionas_lines(chunks, partials=None)Generator yielding complete lines from arbitrary pieces of text from
the iterable ofstrchunks.After completion, any remaining newline-free chunks remain
in the partials list; they will be unavailable to the caller
unless the list is presupplied.Functioncamelcase(snakecased, first_letter_only=False)Convert a snake cased stringsnakecasedinto camel case.Parameters:snakecased: the snake case string to convertfirst_letter_only: optional flag (defaultFalse);
if true then just ensure that the first character of a word
is uppercased, otherwise usestr.titleExample:>>> camelcase('abc_def')
'abcDef'
>>> camelcase('ABc_def')
'abcDef'
>>> camelcase('abc_dEf')
'abcDef'
>>> camelcase('abc_dEf', first_letter_only=True)
'abcDEf'Functioncommon_prefix(*strs)Return the common prefix of the stringsstrs.Examples:>>> common_prefix('abc', 'def')
''
>>> common_prefix('abc', 'abd')
'ab'
>>> common_prefix('abc', 'abcdef')
'abc'
>>> common_prefix('abc', 'abcdef', 'abz')
'ab'
>>> # contrast with cs.fileutils.common_path_prefix
>>> common_prefix('abc/def', 'abc/def1', 'abc/def2')
'abc/def'Functioncommon_suffix(*strs)Return the common suffix of the stringsstrs.Functioncropped(s: str, max_length: int = 32, roffset: int = 1, ellipsis: str = '...')If the length ofsexceedsmax_length(default32),
replace enough of the tail withellipsisand the lastroffset(default1) characters ofsto fit inmax_lengthcharacters.Functioncropped_repr(o, roffset=1, max_length=32, inner_max_length=None)Compute a croppedrepr()ofo.Parameters:o: the object to representmax_length: the maximum length of the representation, default32inner_max_length: the maximum length of the representations
of members ofo, defaultmax_length//2roffset: the number of trailing characters to preserve, default1Functioncutprefix(s, prefix)Strip aprefixfrom the front ofs.
Return the suffix ifs.startswith(prefix), elses.Example:>>> abc_def = 'abc.def'
>>> cutprefix(abc_def, 'abc.')
'def'
>>> cutprefix(abc_def, 'zzz.')
'abc.def'
>>> cutprefix(abc_def, '.zzz') is abc_def
TrueFunctioncutsuffix(s, suffix)Strip asuffixfrom the end ofs.
Return the prefix ifs.endswith(suffix), elses.Example:>>> abc_def = 'abc.def'
>>> cutsuffix(abc_def, '.def')
'abc'
>>> cutsuffix(abc_def, '.zzz')
'abc.def'
>>> cutsuffix(abc_def, '.zzz') is abc_def
TrueClassFFloat(FNumericMixin, FormatableMixin, FormatableFormatter, string.Formatter, builtins.float)Formattablefloat.ClassFInt(FNumericMixin, FormatableMixin, FormatableFormatter, string.Formatter, builtins.int)Formattableint.ClassFNumericMixin(FormatableMixin, FormatableFormatter, string.Formatter)AFormatableMixinsubclass.Functionformat_as(format_s: str, format_mapping, formatter=None, error_sep=None, strict=None)Format the stringformat_susingFormatter.vformat,
return the formatted result.
This is a wrapper forstr.format_mapwhich raises a more informativeFormatAsErrorexception on failure.Parameters:format_s: the format string to use as the templateformat_mapping: the mapping of available replacement fieldsformatter: an optionalstring.Formatter-like instance
with a.vformat(format_string,args,kwargs)method,
usually a subclass ofstring.Formatter;
if not specified thenFormatableFormatteris usederror_sep: optional separator for the multipart error message,
default fromFormatAsError.DEFAULT_SEPARATOR:'; 'strict: optional flag (defaultFalse)
indicating that an unresolveable field should raise aKeyErrorinstead of inserting a placeholderFunctionformat_attribute(method)A decorator to mark a method as available as a format method.
Requires the enclosing class to be decorated with@has_format_attributes.For example,
theFormatableMixin.jsonmethod is defined like this:@format_attribute
def json(self):
    return self.FORMAT_JSON_ENCODER.encode(self)which allows aFormatableMixinsubclass instance
to be used in a format string like this:{instance:json}to insert a JSON transcription of the instance.It is recommended that methods marked with@format_attributehave no side effects and do not modify state,
as they are intended for use in ad hoc format strings
supplied by an end user.Functionformat_escape(s)Escape{}characters in a string to protect them fromstr.format.Functionformat_recover(*da, **dkw)Decorator for__format__methods which replaces failed formats
with{self:format_spec}.ClassFormatableFormatter(string.Formatter)Astring.Formattersubclass interacting with objects
which inherit fromFormatableMixin.ClassFormatableMixin(FormatableFormatter, string.Formatter)A subclass ofFormatableFormatterwhich  provides 2 features:a__format__method which parses theformat_specstring
into multiple colon separated terms whose results chainaformat_asmethod which formats a format string usingstr.format_mapwith a suitable mapping derived from the instance
via itsformat_kwargsmethod
(whose default is to return the instance itself)Theformat_asmethod is like an inside outstr.formatorobject.__format__method.Thestr.formatmethod is designed for formatting a string
from a variety of other objects supplied in the keyword arguments.Theobject.__format__method is for filling out a singlestr.formatreplacement field from a single object.By contrast,format_asis designed to fill out an entire format
string from the current object.For example, thecs.tagset.TagSetMixinclass
usesFormatableMixinto provide aformat_asmethod
whose replacement fields are derived from the tags in the tag set.Subclasses wanting to provide additionalformat_specterms
should:overrideFormatableFormatter.format_field1to implement
terms with no colons, lettingformat_fielddo the split into termsoverrideFormatableFormatter.get_format_subspecsto implement
the parse offormat_specinto a sequence of terms.
This might recognise a special additional syntax
and quietly fall back tosuper().get_format_subspecsif that is not present.ClassFormatAsError(builtins.LookupError, builtins.Exception, builtins.BaseException)Subclass ofLookupErrorfor use byformat_as.ClassFStr(FormatableMixin, FormatableFormatter, string.Formatter, builtins.str)Astrsubclass with theFormatableMixinmethods,
particularly its__format__method
which usesstrmethod names as valid formats.It also has a bunch of utility methods which are available
as:methodin format strings.Functionget_chars(s, offset, gochars)Scan the stringsfor characters ingocharsstarting atoffset.
Return(match,new_offset).gocharsmay also be a callable, in which case a characterchis accepted ifgochars(ch)is true.Functionget_decimal(s, offset=0)Scan the stringsfor decimal characters starting atoffset(default0).
Return(dec_string,new_offset).Functionget_decimal_or_float_value(s, offset=0)Fetch a decimal or basic float (nnn.nnn) value
from the strsatoffset(default0).
Return(value,new_offset).Functionget_decimal_value(s, offset=0)Scan the stringsfor a decimal value starting atoffset(default0).
Return(value,new_offset).Functionget_delimited(s, offset, delim)Collect text from the stringsfrom positionoffsetup
to the first occurence of delimiterdelim; return the text
excluding the delimiter and the offset after the delimiter.Functionget_dotted_identifier(s, offset=0, **kw)Scan the stringsfor a dotted identifier (by default an
ASCII letter or underscore followed by letters, digits or
underscores) with optional trailing dot and another dotted
identifier, starting atoffset(default0).
Return(match,new_offset).Note: the empty string and an unchanged offset will be returned if
there is no leading letter/underscore.Keyword arguments are passed toget_identifier(used for each component of the dotted identifier).Functionget_envvar(s, offset=0, environ=None, default=None, specials=None)Parse a simple environment variable reference to $varname or
$x where "x" is a special character.Parameters:s: the string with the variable referenceoffset: the starting point for the referencedefault: default value for missing environment variables;
ifNone(the default) aValueErroris raisedenviron: the environment mapping, defaultos.environspecials: the mapping of special single character variablesFunctionget_hexadecimal(s, offset=0)Scan the stringsfor hexadecimal characters starting atoffset(default0).
Return(hex_string,new_offset).Functionget_hexadecimal_value(s, offset=0)Scan the stringsfor a hexadecimal value starting atoffset(default0).
Return(value,new_offset).Functionget_identifier(s, offset=0, alpha='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', number='0123456789', extras='_')Scan the stringsfor an identifier (by default an ASCII
letter or underscore followed by letters, digits or underscores)
starting atoffset(default 0).
Return(match,new_offset).Note: the empty string and an unchanged offset will be returned if
there is no leading letter/underscore.Parameters:s: the string to scanoffset: the starting offset, default0.alpha: the characters considered alphabetic,
defaultstring.ascii_letters.number: the characters considered numeric,
defaultstring.digits.extras: extra characters considered part of an identifier,
default'_'.Functionget_ini_clause_entryname(s, offset=0)Parse a[clausename]entrynamestring
fromsatoffset(default0).
Return(clausename,entryname,new_offset).Functionget_ini_clausename(s, offset=0)Parse a[clausename]string fromsatoffset(default0).
Return(clausename,new_offset).Functionget_nonwhite(s, offset=0)Scan the stringsfor characters not instring.whitespacestarting atoffset(default0).
Return(match,new_offset).Functionget_other_chars(s, offset=0, stopchars=None)Scan the stringsfor characters not instopcharsstarting
atoffset(default0).
Return(match,new_offset).Functionget_prefix_n(s, prefix, n=None, *, offset=0)Strip a leadingprefixand numeric valuenfrom the stringsstarting atoffset(default0).
Return the matched prefix, the numeric value and the new offset.
Returns(None,None,offset)on no match.Parameters:s: the string to parseprefix: the prefix string which must appear atoffsetor an object with amatch(str,offset)method
such as anre.Patternregexp instancen: optional integer value;
if omitted any value will be accepted, otherwise the numeric
part must matchnIfprefixis astr, the "matched prefix" return value isprefix.
Otherwise the "matched prefix" return value is the result of
theprefix.match(s,offset)call. The result must also support
a.end()method returning the offset insbeyond the match,
used to locate the following numeric portion.Examples:import re
get_prefix_n('s03e01--', 's')
('s', 3, 3)
get_prefix_n('s03e01--', 's', 3)
('s', 3, 3)
get_prefix_n('s03e01--', 's', 4)
(None, None, 0)
get_prefix_n('s03e01--', re.compile('[es]',re.I))
(<re.Match object; span=(0, 1), match='s'>, 3, 3)
get_prefix_n('s03e01--', re.compile('[es]',re.I), offset=3)
(<re.Match object; span=(3, 4), match='e'>, 1, 6)Functionget_qstr(s, offset=0, q='"', environ=None, default=None, env_specials=None)Get quoted text with slosh escapes and optional environment substitution.Parameters:s: the string containg the quoted text.offset: the starting point, default0.q: the quote character, default'"'. IfqisNone,
do not expect the string to be delimited by quote marks.environ: if notNone, also parse and expand$envvarreferences.default: passed toget_envvarFunctionget_qstr_or_identifier(s, offset)Parse a double quoted string or an identifier.Functionget_sloshed_text(s, delim, offset=0, slosh='\\', mapper=<function slosh_mapper at 0x10371f1f0>, specials=None)Collect slosh escaped text from the stringsfrom positionoffset(default0) and return the decoded unicode string and
the offset of the completed parse.Parameters:delim: end of string delimiter, such as a single or double quote.offset: starting offset withins, default0.slosh: escape character, default a slosh ('').mapper: a mapping function which accepts a single character
and returns a replacement string orNone; this is used the
replace things such as '\t' or '\n'. The default is theslosh_mapperfunction, whose default mapping isSLOSH_CHARMAP.specials: a mapping of other special character sequences and parse
functions for gathering them up. When one of the special
character sequences is found in the string, the parse
function is called to parse at that point.
The parse functions acceptsand the offset of the special character. They return
the decoded string and the offset past the parse.The escape charactersloshintroduces an encoding of some
replacement text whose value depends on the following character.
If the following character is:the escape characterslosh, insert the escape character.the string delimiterdelim, insert the delimiter.the character 'x', insert the character with code from the following
2 hexadecimal digits.the character 'u', insert the character with code from the following
4 hexadecimal digits.the character 'U', insert the character with code from the following
8 hexadecimal digits.a character from the keys ofmapperFunctionget_tokens(s, offset, getters)Parse the stringsfrom positionoffsetusing the supplied
tokeniser functionsgetters.
Return the list of tokens matched and the final offset.Parameters:s: the string to parse.offset: the starting position for the parse.getters: an iterable of tokeniser specifications.Each tokeniser specificationgetteris either:a callable expecting(s,offset)and returning(token,new_offset)a literal string, to be matched exactlyatupleorlistwith values(func,args,kwargs);
callfunc(s,offset,*args,**kwargs)an object with a.matchmethod such as a regex;
callgetter.match(s,offset)and return a match object with
a.end()method returning the offset of the end of the matchFunctionget_uc_identifier(s, offset=0, number='0123456789', extras='_')Scan the stringsfor an identifier as forget_identifier,
but require the letters to be uppercase.Functionget_white(s, offset=0)Scan the stringsfor characters instring.whitespacestarting atoffset(default0).
Return(match,new_offset).Functionhas_format_attributes(*da, **dkw)Class decorator to walk this class for direct methods
marked as for use in format strings
and to include them incls.format_attributes().Methods are normally marked with the@format_attributedecorator.Ifinheritis true the base format attributes will be
obtained from other classes:inheritisTrue: usecls.__mro__inheritis a class: use that classotherwise assumeinheritis an iterable of classes
For each classotherclass, update the initial attribute
mapping fromotherclass.get_format_attributes().Functionhexify(bs)A flavour ofbinascii.hexlifyreturning astr.Functionhtmlify(s, nbsp=False)Convert a string for safe transcription in HTML.Parameters:s: the stringnbsp: replaces spaces with"&nbsp;"to prevent word folding,
defaultFalse.Functionhtmlquote(s)Quote a string for use in HTML.Functionis_dotted_identifier(s, offset=0, **kw)Test if the stringsis an identifier from positionoffsetonward.Functionis_identifier(s, offset=0, **kw)Test if the stringsis an identifier
from positionoffset(default0) onward.Functionis_uc_identifier(s, offset=0, **kw)Test if the stringsis an uppercase identifier
from positionoffset(default0) onward.FunctionisUC_(s)Check that a string matches the regular expression^[A-Z][A-Z_0-9]*$.Functionjsquote(s)Quote a string for use in JavaScript.Functionlc_(value)Returnvalue.lower()with'-'translated into'_'and' 'translated into'-'.I use this to construct lowercase filenames containing a
readable transcription of a title string.See alsotitleify_lc(), an imperfect reversal of this.Functionmatch_tokens(s, offset, getters)Wrapper forget_tokenswhich catchesValueErrorexceptions
and returns(None,offset).FunctionparseUC_sAttr(attr)Take an attribute nameattrand return(key,is_plural).Examples:'FOO'returns('FOO',False).'FOOs'or'FOOes'returns('FOO',True).
Otherwise return(None,False).Functionphpquote(s)Quote a string for use in PHP code.Functionr(o, max_length=None, *, use_cls=False)Liketyped_strbut usingreprinstead ofstr.
This is available as bothtyped_reprandr.Functions(o, use_cls=False, use_repr=False, max_length=32)Return "type(o).name:str(o)" for some objecto.
This is available as bothtyped_strands.Parameters:use_cls: defaultFalse;
if true, usestr(type(o))instead oftype(o).__name__use_repr: defaultFalse;
if true, userepr(o)instead ofstr(o)I use this a lot when debugging. Example:from cs.lex import typed_str as s
......
X("foo = %s", s(foo))Functionskipwhite(s, offset=0)Convenience routine for skipping past whitespace;
returns the offset of the next nonwhitespace character.Functionslosh_mapper(c, charmap=None)Return a string to replace backslash-c, orNone.Functionsnakecase(camelcased)Convert a camel cased stringcamelcasedinto snake case.Parameters:cameelcased: the cameel case string to convertfirst_letter_only: optional flag (defaultFalse);
if true then just ensure that the first character of a word
is uppercased, otherwise usestr.titleExample:>>> snakecase('abcDef')
'abc_def'
>>> snakecase('abcDEf')
'abc_def'
>>> snakecase('AbcDef')
'abc_def'Functionsplit_remote_path(remotepath: str) -> Tuple[Optional[str], str]Split a path with an optional leading[user@]rhost:prefix
into the prefix and the remaining path.Noneis returned for the prefix is there is none.
This is useful for things likersynctargets etc.Functionstripped_dedent(s)Slightly smarter dedent which ignores a string's opening indent.Algorithm:
strip the supplied strings, pull off the leading line,
dedent the rest, put back the leading line.This supports my preferred docstring layout, where the opening
line of text is on the same line as the opening quote.Example:>>> def func(s):
...   """ Slightly smarter dedent which ignores a string's opening indent.
...       Strip the supplied string `s`. Pull off the leading line.
...       Dedent the rest. Put back the leading line.
...   """
...   pass
...
>>> from cs.lex import stripped_dedent
>>> print(stripped_dedent(func.__doc__))
Slightly smarter dedent which ignores a string's opening indent.
Strip the supplied string `s`. Pull off the leading line.
Dedent the rest. Put back the leading line.Functionstrlist(ary, sep=', ')Convert an iterable to strings and join withsep(default', ').Functiontabpadding(padlen, tabsize=8, offset=0)Compute some spaces to use a tab padding at an offfset.Functiontexthexify(bs, shiftin='[', shiftout=']', whitelist=None)Transcribe the bytesbsto text using compact text runs for
some common text values.This can be reversed with theuntexthexifyfunction.This is an ad doc format devised to be compact but also to
expose "text" embedded within to the eye. The original use
case was transcribing a binary directory entry format, where
the filename parts would be somewhat visible in the transcription.The output is a string of hexadecimal digits for the encoded
bytes except for runs of values from the whitelist, which are
enclosed in the shiftin and shiftout markers and transcribed
as is. The default whitelist is values of the ASCII letters,
the decimal digits and the punctuation characters '_-+.,'.
The default shiftin and shiftout markers are '[' and ']'.String objects converted with eitherhexifyandtexthexifyoutput strings may be freely concatenated and decoded withuntexthexify.Example:>>> texthexify(b'&^%&^%abcdefghi)(*)(*')
'265e25265e25[abcdefghi]29282a29282a'Parameters:bs: the bytes to transcribeshiftin: Optional. The marker string used to indicate a shift to
direct textual transcription of the bytes, default:'['.shiftout: Optional. The marker string used to indicate a
shift from text mode back into hexadecimal transcription,
default']'.whitelist: an optional bytes or string object indicating byte
values which may be represented directly in text;
the default value is the ASCII letters, the decimal digits
and the punctuation characters'_-+.,'.Functiontitleify_lc(value_lc)Translate'-'into' 'and'_'translated into'-',
then titlecased.See alsolc_(), which this reverses imperfectly.Functiontyped_repr(o, max_length=None, *, use_cls=False)Liketyped_strbut usingreprinstead ofstr.
This is available as bothtyped_reprandr.Functiontyped_str(o, use_cls=False, use_repr=False, max_length=32)Return "type(o).name:str(o)" for some objecto.
This is available as bothtyped_strands.Parameters:use_cls: defaultFalse;
if true, usestr(type(o))instead oftype(o).__name__use_repr: defaultFalse;
if true, userepr(o)instead ofstr(o)I use this a lot when debugging. Example:from cs.lex import typed_str as s
......
X("foo = %s", s(foo))Functionunctrl(s, tabsize=8)Return the stringswithTABs expanded and control characters
replaced with printable representations.Functionuntexthexify(s, shiftin='[', shiftout=']')Decode a textual representation of binary data into binary data.This is the reverse of thetexthexifyfunction.Outside of theshiftin/shiftoutmarkers the binary data
are represented as hexadecimal. Within the markers the bytes
have the values of the ordinals of the characters.Example:>>> untexthexify('265e25265e25[abcdefghi]29282a29282a')
b'&^%&^%abcdefghi)(*)(*'Parameters:s: the string containing the text representation.shiftin: Optional. The marker string commencing a sequence
of direct text transcription, default'['.shiftout: Optional. The marker string ending a sequence
of direct text transcription, default']'.Release LogRelease 20240211:
New split_remote_path() function to recognise [[user@]host]:path.Release 20231018:
New is_uc_identifier function.Release 20230401:
Import update.Release 20230217.1:
Fix package requirements.Release 20230217:New get_prefix_n function to parse a numeric value preceeded by a prefix.Drop strip_prefix_n, get_prefix_n is more general and I had not got around to using strip_prefix_n yet - when I did, I ended up writing get_prefix_n.Release 20230210:@has_format_attributes: new optional inherit parameter to inherit superclass (or other) format attributes, default False.New FNumericMixin, FFloat, FInt FormatableMixin subclasses like FStr - they add .localtime and .utctime formattable attributes.Release 20220918:
typed_str(): crop the value part, default max_length=32, bugfix message cropping.Release 20220626:Remove dependency on cs.py3, we've been Python 2 incompatible for a while.FormatableFormatter.format_field: promote None to FStr(None).Release 20220227:typed_str,typed_repr: make max_length the first optional positional parameter, make other parameters keyword only.New camelcase() and snakecase() functions.Release 20211208:
Docstring updates.Release 20210913:FormatableFormatter.FORMAT_RE_ARG_NAME_s: strings commencing with digits now match \d+(.\d+)[a-z]+, eg "02d".Alias typed_str as s and typed_repr as r.FormatableFormatter: new .format_mode thread local state object initially with strict=False, used to control whether unknown fields leave a placeholder or raise KeyError.FormatableFormatter.format_field: assorted fixes.Release 20210906:
New strip_prefix_n() function to strip a leadingprefixand numeric valuenfrom the start of a string.Release 20210717:Many many changes to FormatableMixin, FormatableFormatter and friends around supporting {foo|conv1|con2|...} instead of {foo!conv}. Still in flux.New typed_repr like typed_str but using repr.Release 20210306:New cropped() function to crop strings.Rework cropped_repr() to do the repr() itself, and to crop the interiors of tuples and lists.cropped_repr: new inner_max_length for cropping the members of collections.cropped_repr: special case for length=1 tuples.New typed_str(o) object returning type(o).name:str(o) in the default case, useful for debugging.Release 20201228:
Minor doc updates.Release 20200914:Hide terribly special purpose lastlinelen() in cs.hier under a private name.New common_prefix and common_suffix function to compare strings.Release 20200718:
get_chars: accept a callable for gochars, indicating a per character test function.Release 20200613:
cropped_repr: replace hardwired 29 with computed lengthRelease 20200517:New get_ini_clausename to parse "[clausename]".New get_ini_clause_entryname parsing "[clausename]entryname".New cropped_repr for returning a shortened repr()+"..." if the length exceeds a threshold.New format_escape function to double {} characters to survive str.format.Release 20200318:New lc_() function to lowercase and dash a string, new titleify_lc() to mostly reverse lc_().New format_as function, FormatableMixin and related FormatAsError.Release 20200229:
New cutprefix and cutsuffix functions.Release 20190812:
Fix bad slosh escapes in strings.Release 20190220:
New function get_qstr_or_identifier.Release 20181108:
new function get_decimal_or_float_value to read a decimal or basic floatRelease 20180815:
No semantic changes; update some docstrings and clean some lint, fix a unit test.Release 20180810:New get_decimal_value and get_hexadecimal_value functions.New stripped_dedent function, a slightly smarter textwrap.dedent.Release 20171231:
New function get_decimal. Drop unused function dict2js.Release 20170904:
Python 2/3 ports, move rfc2047 into new cs.rfc2047 module.Release 20160828:Use "install_requires" instead of "requires" in DISTINFO.Discard str1(), pointless optimisation.unrfc2047: map _ to SPACE, improve exception handling.Add phpquote: quote a string for use in PHP code; add docstring to jsquote.Add is_identifier test.Add get_dotted_identifier.Add is_dotted_identifier.Add get_hexadecimal.Add skipwhite, convenince wrapper for get_white returning just the next offset.Assorted bugfixes and improvements.Release 20150120:
cs.lex: texthexify: backport to python 2 using cs.py3 bytes typeRelease 20150118:
metadata updatesRelease 20150116:
PyPI metadata and slight code cleanup.