Skip to content

Instantly share code, notes, and snippets.

@fish2000
Last active May 10, 2019 01:29
Show Gist options
  • Save fish2000/51cf4ea3977abbd7ea6ce74c442eb870 to your computer and use it in GitHub Desktop.
Save fish2000/51cf4ea3977abbd7ea6ce74c442eb870 to your computer and use it in GitHub Desktop.
Generic Python REPL configuration NOW WITH ANSI-COLOR FIGLET BANNERS DOGG
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import plistlib
import zict
import os
import appdirs
from replutilities import attr, isstring, isbytes
# UTILITY STUFF: Exceptions
class KeyValueError(ValueError):
pass
# UTILITY STUFF: Directory class
try:
from instakit.utils.filesystem import Directory
except (ImportError, SyntaxError):
class Directory(object):
def __init__(self, pth=None):
self.target = pth and str(pth) or os.getcwd()
@property
def name(self):
return self.target
@property
def exists(self):
return os.path.isdir(self.target)
def makedirs(self, pth=None):
os.makedirs(os.path.abspath(
os.path.join(self.name, pth or os.curdir)), exist_ok=False)
return self
def __str__(self):
return self.target
# UTILITY STUFF: AppDirs wrapper
class ReplEnvDirs(appdirs.AppDirs):
def __init__(self):
""" Initialize with a fixed “appname” parameter `replenv` """
super(ReplEnvDirs, self).__init__(appname='replenv')
self.mode = 'linux' # use Linux directory layout
@property
def mode(self):
""" The “appdirs.system” global module variable controls the
operation of the properties of all `appdirs.AppDirs` instances.
"""
return appdirs.system
@mode.setter
def mode(self, value):
if value not in ('darwin', 'linux'):
raise ValueError("invalid mode: %s" % value)
appdirs.system = value
@property
def site_config(self):
return Directory(self.site_config_dir)
@property
def site_data(self):
return Directory(self.site_data_dir)
@property
def user_cache(self):
return Directory(self.user_cache_dir)
@property
def user_config(self):
return Directory(self.user_config_dir)
@property
def user_data(self):
return Directory(self.user_data_dir)
@property
def user_log(self):
return Directory(self.user_log_dir)
@property
def user_state(self):
return Directory(self.user_state_dir)
renvdirs = ReplEnvDirs()
if not renvdirs.user_config.exists:
renvdirs.user_config.makedirs()
ENCODING = 'UTF-8'
zfile = zict.File(str(renvdirs.user_config), mode='a')
zutf8 = zict.Func(dump=attr(plistlib, 'dumps', 'writePlistToString'),
load=attr(plistlib, 'loads', 'readPlistFromString'),
d=zfile)
zfunc = zict.Func(dump=lambda value: isstring(value) and value.encode(ENCODING) or value,
load=lambda value: isbytes(value) and value.decode(ENCODING) or value,
d=zutf8)
def has(key):
""" Test if a key is contained in the key-value store. """
return key in zfunc
def count():
""" Return the number of items in the key-value store. """
return len(zfunc)
def get(key, default_value=None):
""" Return a value from the ReplEnv user-config key-value store. """
if not key:
return default_value
try:
return zfunc[key]
except KeyError:
return default_value
def set(key, value):
""" Set and return a value in the ReplEnv user-config key-value store. """
if not key:
raise KeyValueError("Non-Falsey key required (k: %s, v: %s)" % (key, value))
if not value:
raise KeyValueError("Non-Falsey value required (k: %s, v: %s)" % (key, value))
zfunc[key] = value
return get(key)
def delete(key):
""" Delete a value from the ReplEnv user-config key-value store. """
if not key:
raise KeyValueError("Non-Falsey key required for deletion (k: %s)" % key)
del zfunc[key]
def iterate():
""" Return an iterator for the key-value store. """
return iter(zfunc)
def keys():
""" Return an iterable with all of the keys in the key-value store. """
return zfunc.keys()
def values():
""" Return an iterable with all of the values in the key-value store. """
return zfunc.values()
def items():
""" Return an iterable yielding (key, value) for all items in the key-value store. """
return zfunc.items()
__dir__ = lambda: ('KeyValueError',
'has', 'count',
'get', 'set', 'delete', 'iterate',
'keys', 'values', 'items')
# -*- encoding: utf-8 -*-
"""
replenv.py
• Default module imports and other configgy code,
• For generic use in python repls – both the python2 and python3 interactive
interpreters, as well as bpython, ipython, ptpython, pyzo, PyCharm, and
whatever else; code specific to any given repl module lives in the config
files, per whatever that packages’ demands may be.
Created by FI$H 2000 on 2019-02-27.
Copyright (c) 2012-2025 Objects In Space And Time, LLC. All rights reserved.
"""
from __future__ import print_function, unicode_literals
if __name__ == '__main__':
# take your executions elsewhere:
raise RuntimeError(
"%s is for module import or star-import only" % __file__)
# Python version figlet banners:
banners = {}
banners['3.x'] = """
888 888 .d8888b.
888 888 d88P Y88b
888 888 .d88P
88888b. 888 888 888888 88888b. .d88b. 88888b. 8888" 888 888
888 "88b 888 888 888 888 "88b d88""88b 888 "88b "Y8b. `Y8bd8P'
888 888 888 888 888 888 888 888 888 888 888 888 888 X88K
888 d88P Y88b 888 Y88b. 888 888 Y88..88P 888 888 Y88b d88P d8b .d8""8b.
88888P" "Y88888 "Y888 888 888 "Y88P" 888 888 "Y8888P" Y8P 888 888
888 888
888 Y8b d88P
888 "Y88P"
"""
banners['3.8'] = """
888 888 .d8888b. .d8888b.
888 888 d88P Y88b d88P Y88b
888 888 .d88P Y88b .d88P
88888b. 888 888 888888 88888b. .d88b. 88888b. 8888" "888888"
888 "88b 888 888 888 888 "88b d88""88b 888 "88b "Y8b. .d8Y""Y8b.
888 888 888 888 888 888 888 888 888 888 888 888 888 888 888
888 d88P Y88b 888 Y88b. 888 888 Y88..88P 888 888 Y88b d88P d8b Y88b d88P
88888P" "Y88888 "Y888 888 888 "Y88P" 888 888 "Y8888P" Y8P "Y8888P"
888 888
888 Y8b d88P
888 "Y88P"
"""
banners['3.7'] = """
888 888 .d8888b. 8888888888
888 888 d88P Y88b d88P
888 888 .d88P d88P
88888b. 888 888 888888 88888b. .d88b. 88888b. 8888" d88P
888 "88b 888 888 888 888 "88b d88""88b 888 "88b "Y8b. 88888888
888 888 888 888 888 888 888 888 888 888 888 888 888 d88P
888 d88P Y88b 888 Y88b. 888 888 Y88..88P 888 888 Y88b d88P d8b d88P
88888P" "Y88888 "Y888 888 888 "Y88P" 888 888 "Y8888P" Y8P d88P
888 888
888 Y8b d88P
888 "Y88P"
"""
banners['2.7'] = """
888 888 .d8888b. 8888888888
888 888 d88P Y88b d88P
888 888 888 d88P
88888b. 888 888 888888 88888b. .d88b. 88888b. .d88P d88P
888 "88b 888 888 888 888 "88b d88""88b 888 "88b .od888P" 88888888
888 888 888 888 888 888 888 888 888 888 888 d88P" d88P
888 d88P Y88b 888 Y88b. 888 888 Y88..88P 888 888 888" d8b d88P
88888P" "Y88888 "Y888 888 888 "Y88P" 888 888 888888888 Y8P d88P
888 888
888 Y8b d88P
888 "Y88P"
"""
# Add miscellaneous necessities:
from PIL import Image
from pprint import pprint, pformat
import sys, os, re
import appdirs
import argparse
import collections
import colorama
import colorio
import colormath
import contextlib
import copy
import datetime
import dateutil
import decimal
import functools
import inspect
import itertools
import math
import numpy
import requests
import shutil
import six
import sysconfig
import termcolor
import types
import xerox
# Configure ANSI-color python banner, per python version:
if six.PY3:
banner = banners.get('3.%s' % sys.version_info.minor, banners['3.x'])
banner_color = colorama.Fore.CYAN
else:
banner = banners['2.7']
banner_color = colorama.Fore.LIGHTGREEN_EX
def print_python_banner(text, color,
reset=colorama.Style.RESET_ALL):
for line in text.splitlines():
print(color + line, sep='')
print(reset, end='')
def print_warning(text, color=colorama.Fore.RED,
reset=colorama.Style.RESET_ALL):
print(color + text, sep='')
print(reset, end='')
# Practice safe star-importing:
__all__ = ('Image',
'pprint', 'pformat',
'sys', 'os', 're',
'appdirs',
'argparse',
'collections',
'colorama',
'colorio',
'colormath',
'contextlib',
'copy',
'datetime',
'dateutil',
'decimal',
'functools',
'inspect',
'itertools',
'math',
'numpy',
'reduce',
'requests',
'shutil',
'six',
'sysconfig',
'termcolor',
'types',
'xerox',
'print_python_banner',
'print_warning', 'banner',
'banner_color',
'modules')
python2_expires = 'January 1st, 2020'
is_python2_dead = datetime.datetime.now() >= dateutil.parser.parse(python2_expires)
try:
from functools import reduce
except (ImportError, SyntaxError):
pass
try:
if six.PY3:
from replpy3 import *
except (AttributeError, SyntaxError):
pass
else:
if six.PY3:
__all__ += (u'Σ',)
try:
import abc
import collections.abc as collectionsabc
import asciiplotlib
except (ImportError, SyntaxError):
pass
else:
# Extend `__all__`:
__all__ += ('abc', 'collectionsabc', 'asciiplotlib')
try:
from halogen.filesystem import (which, TemporaryName, Directory,
TemporaryDirectory,
TemporaryNamedFile)
except (ImportError, SyntaxError):
pass
else:
# Extend `__all__`:
__all__ += ('which', 'TemporaryName', 'Directory',
'TemporaryDirectory',
'TemporaryNamedFile')
try:
from instakit.utils.static import asset
except (ImportError, SyntaxError):
pass
else:
# Extend `__all__`:
__all__ += ('asset', 'image_paths', 'catimage')
# Prepare a list of readily open-able image file paths:
image_paths = list(map(
lambda image_file: asset.path('img', image_file),
asset.listfiles('img')))
# I do this practically every time, so I might as well do it here:
catimage = Image.open(image_paths[0])
# `__dir__` listifies `__all__`:
__dir__ = lambda: list(__all__)
modules = tuple(__dir__())
# Print python banner before end-of-module --
# if running in TextMate, we use `sys.stderr` instead of ANSI colors,
# as that’s the only way to get any sort of colored output in TextMate’s
# console output window:
if os.environ.get('TM_PYTHON'):
print(banner, file=sys.stderr)
else:
colorama.init()
print_python_banner(banner, banner_color)
if not six.PY3:
if is_python2_dead:
warning = u"∞§• ¡LOOK OUT! Python 2.x has been officially declared DEAD!!!!!!!"
else:
warning = u"∞§• ¡BEWARE! Python 2.x will perish when the clock strikes 2020!!!"
if os.environ.get('TM_PYTHON'):
print(warning, file=sys.stderr)
else:
print_warning(warning)
# -*- encoding: utf-8 -*-
from __future__ import print_function, unicode_literals
try:
from functools import reduce
except (ImportError, SyntaxError):
pass
Σ = reduce
__all__ = (u'Σ',)
__dir__ = lambda: list(__all__)
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
from __future__ import print_function
import sys
try:
from pathlib import Path
except ImportError:
try:
from pathlib2 import Path
except ImportError:
Path = None
# N.B. this may or may not be a PY2/PY3 thing:
maxint = getattr(sys, 'maxint',
getattr(sys, 'maxsize', (2 ** 64) / 2))
import io, os, re, six
import argparse
import array
import contextlib
import decimal
import numpy
__exports__ = {}
def doctrim(docstring):
""" This function is straight outta PEP257 -- q.v. `trim(…)`,
“Handling Docstring Indentation” subsection sub.:
https://www.python.org/dev/peps/pep-0257/#id18
"""
if not docstring:
return ''
# Convert tabs to spaces (following the normal Python rules)
# and split into a list of lines:
lines = docstring.expandtabs().splitlines()
# Determine minimum indentation (first line doesn't count):
indent = maxint
for line in lines[1:]:
stripped = line.lstrip()
if stripped:
indent = min(indent, len(line) - len(stripped))
# Remove indentation (first line is special):
trimmed = [lines[0].strip()]
if indent < maxint:
for line in lines[1:]:
trimmed.append(line[indent:].rstrip())
# Strip off trailing and leading blank lines:
while trimmed and not trimmed[-1]:
trimmed.pop()
while trimmed and not trimmed[0]:
trimmed.pop(0)
# Return a single string:
return '\n'.join(trimmed)
def determine_name(thing, name=None, try_repr=False):
""" Private module function to find a name for a thing. """
if name is not None:
return name
code = None
if hasattr(thing, '__export_name__'):
# q.v. “export(…)” deco-function sub.
if thing.__export_name__:
return thing.__export_name__
if hasattr(thing, '__code__'):
# Python 3.x function code object
code = thing.__code__
elif hasattr(thing, 'func_code'):
# Python 2.x function code object
code = thing.func_code
if code is not None:
if hasattr(code, 'co_name'):
# Use the code objects’ name
name = code.co_name
else:
# Try either __qualname__ or __name__
if hasattr(thing, '__qualname__'):
name = thing.__qualname__
elif hasattr(thing, '__name__'):
name = thing.__name__
if try_repr and name is None:
return repr(thing)
return name
λ = determine_name(lambda: None)
class ExportError(NameError):
pass
def export(thing, name=None, *, doc=None):
""" Add a function -- or any object, really, to the export list.
Exported items will end up wih their names in the modules’
`__all__` tuple, and will also be named in the list returned
by the modules’ `__dir__()` function.
Use export as a decorator to a function definition:
@export
def yo_dogg(i_heard=None):
...
… or manually, to export anything that doesn’t have a name:
yo_dogg = lambda i_heard=None: ...
dogg_heard_index = ( ... )
export(yo_dogg, name="yo_dogg")
export(dogg_heard_index, name="dogg_heard_index")
"""
# Access the module-namespace __exports__ dict:
global __exports__
# No explict name was passed -- try to determine one:
named = determine_name(thing, name=name)
# Double-check our determined name and item before stowing:
if named is None:
raise ExportError("can’t export an unnamed thing")
if named == λ:
raise ExportError("can’t export an unnamed lambda")
if named in __exports__:
raise ExportError("can’t re-export name “%s”" % named)
if thing is __exports__:
raise ExportError("can’t export the __export__ dict directly")
# At this point, “named” is valid -- if we were passed
# a lambda, try to rename it with our valid name:
if callable(thing):
if getattr(thing, '__name__', '') == λ:
thing.__name__ = thing.__qualname__ = named
# If a “doc” argument was passed in, attempt to assign
# the __doc__ attribute accordingly on the item -- note
# that this won’t work for e.g. slotted, builtin, or C-API
# types that lack mutable __dict__ internals (or at least
# a settable __doc__ slot or established attribute).
if doc is not None:
try:
thing.__doc__ = doctrim(doc)
except AttributeError:
pass
# Stow the item in the global __exports__ dict:
__exports__[named] = thing
# Attempt to assign our name as a private attribute
# on the item -- q.v. __doc__ note supra.
if not hasattr(thing, '__export_name__'):
try:
thing.__export_name__ = named
except AttributeError:
pass
# Return the thing, unchanged (that’s how we decorate).
return thing
# UTILITY FUNCTIONS: helpers for builtin container types:
@export
def tuplize(*items):
""" Return a new tuple containing all non-`None` arguments """
return tuple(item for item in items if item is not None)
@export
def uniquify(*items):
""" Return a tuple with a unique set of all non-`None` arguments """
return tuple(frozenset(item for item in items if item is not None))
@export
def listify(*items):
""" Return a new list containing all non-`None` arguments """
return list(item for item in items if item is not None)
def merge_two(one, two, cls=dict):
""" Merge two dictionaries into an instance of the specified class
Based on this docopt example source: https://git.io/fjCZ6
"""
merged = ((key, one.get(key, None) or two.get(key, None)) \
for key in set(one) | set(two))
return cls(merged)
def merge_as(*dicts, cls=dict):
""" Merge all dictionaries into a new instance of the specified class """
merged = cls()
for d in dicts:
merged = merge_two(merged, d, cls=cls)
return merged
@export
def merge_all(*dicts):
""" Merge all dictionary arguments into a new `dict` instance """
return merge_as(*dicts, cls=dict)
# UTILITY STUFF: SimpleNamespace
@export
class SimpleNamespace(object):
""" Implementation courtesy this SO answer:
• https://stackoverflow.com/a/37161391/298171
"""
__slots__ = tuplize('__dict__', '__weakref__')
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __repr__(self):
keys = sorted(self.__dict__)
items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
return "{}({})".format(type(self).__name__, ", ".join(items))
def __eq__(self, other):
return self.__dict__ == other.__dict__
# UTILITY FUNCTIONS: getattr(…) shortcuts:
or_none = lambda thing, atx: getattr(thing, atx, None)
getpyattr = lambda thing, atx, default_value=None: getattr(thing, '__%s__' % atx, default_value)
accessor = lambda function, thing, *attrs: ([atx for atx in (function(thing, atx) \
for atx in attrs) \
if atx is not None] or [None]).pop(0)
attr = lambda thing, *attrs: accessor(or_none, thing, *attrs)
pyattr = lambda thing, *attrs: accessor(getpyattr, thing, *attrs)
@export
def nameof(thing, fallback=''):
""" Get the name of a thing, according to either:
>>> thing.__qualname__
… or:
>>> thing.__name__
… optionally specifying a fallback string.
"""
return determine_name(thing) or fallback
BUILTINS = ('__builtins__', '__builtin__', 'builtins', 'builtin')
import types as thetypes
types = SimpleNamespace()
typed = re.compile(r"^(?P<typename>\w+)(?:Type)$")
# Fill a namespace with type aliases, minus the fucking 'Type' suffix --
# We know they are types because they are in the fucking “types” module, OK?
# And those irritating four characters take up too much pointless space, if
# you asked me, which you implicitly did by reading the comments in my code,
# dogg.
for typename in dir(thetypes):
if typename.endswith('Type'):
setattr(types, typed.match(typename).group('typename'),
getattr(thetypes, typename))
elif typename not in BUILTINS:
setattr(types, typename, getattr(thetypes, typename))
# UTILITY FUNCTIONS: hasattr(…) shortcuts:
haspyattr = lambda thing, atx: hasattr(thing, '__%s__' % atx)
anyattrs = lambda thing, *attrs: any(hasattr(thing, atx) for atx in attrs)
allattrs = lambda thing, *attrs: all(hasattr(thing, atx) for atx in attrs)
anypyattrs = lambda thing, *attrs: any(haspyattr(thing, atx) for atx in attrs)
allpyattrs = lambda thing, *attrs: all(haspyattr(thing, atx) for atx in attrs)
isiterable = lambda thing: anypyattrs(thing, 'iter', 'getitem')
@export
def graceful_issubclass(thing, *cls_or_tuple):
""" A wrapper for `issubclass()` that tries to work with you. """
length = 0
try:
length = len(cls_or_tuple)
except TypeError:
pass
else:
if length == 1:
cls_or_tuple = cls_or_tuple[0]
else:
cls_or_tuple = tuple(item for item in cls_or_tuple if item is not None)
if (not isinstance(cls_or_tuple, (type, tuple))) \
and isiterable(cls_or_tuple):
cls_or_tuple = tuple(cls_or_tuple)
try:
return issubclass(thing, cls_or_tuple)
except TypeError:
pass
try:
return issubclass(type(thing), cls_or_tuple)
except TypeError:
pass
return None
# UTILITY FUNCTIONS: is<something>() unary-predicates, and utility type-tuples with which
# said predicates use to make their decisions:
isabstractmethod = lambda method: getattr(method, '__isabstractmethod__', False)
isabstract = lambda thing: bool(pyattr(thing, 'abstractmethods', 'isabstractmethod'))
isabstractcontextmanager = lambda cls: graceful_issubclass(cls, contextlib.AbstractContextManager)
iscontextmanager = lambda cls: allpyattrs(cls, 'enter', 'exit') or isabstractcontextmanager(cls)
numeric_types = (int, float, decimal.Decimal)
array_types = (numpy.ndarray,
numpy.matrix,
numpy.ma.MaskedArray, array.ArrayType,
bytearray)
bytes_types = (bytes, bytearray)
string_types = six.string_types
path_classes = tuplize(argparse.FileType, or_none(os, 'PathLike'), Path) # Path may be “None” in disguise
path_types = string_types + bytes_types + path_classes
file_types = (io.TextIOBase, io.BufferedIOBase, io.RawIOBase, io.IOBase)
callable_types = (types.Function,
types.Method,
types.Lambda,
types.BuiltinFunction,
types.BuiltinMethod)
if six.PY3:
callable_types += (
types.Coroutine,
types.ClassMethodDescriptor,
types.MemberDescriptor,
types.MethodDescriptor)
ispathtype = lambda cls: issubclass(cls, path_types)
ispath = lambda thing: graceful_issubclass(thing, path_types) or haspyattr(thing, 'fspath')
isvalidpath = lambda thing: ispath(thing) and os.path.exists(thing)
isnumber = lambda thing: issubclass(thing, numeric_types)
isnumeric = lambda thing: issubclass(thing, numeric_types)
isarray = lambda thing: graceful_issubclass(thing, array_types)
isstring = lambda thing: graceful_issubclass(thing, string_types)
isbytes = lambda thing: graceful_issubclass(thing, bytes_types)
isfunction = lambda thing: graceful_issubclass(thing, types.Function, types.Lambda) or callable(thing)
islambda = lambda thing: pyattr(thing, 'name', 'qualname') == λ
# THE MODULE EXPORTS:
export(λ, name='λ')
export(or_none, name='or_none', doc="or_none(thing, attribute) → shortcut for getattr(thing, attribute, None)")
export(getpyattr, name='getpyattr', doc="getpyattr(thing, attribute[, default]) → shortcut for getattr(thing, '__%s__' % attribute[, default])")
export(accessor, name='accessor', doc="accessor(func, thing, *attributes) → return the first non-None value had by successively applying func(thing, attribute)")
export(attr, name='attr', doc="Return the first existing attribute from a thing, given 1+ attribute names")
export(pyattr, name='pyattr', doc="Return the first existing __special__ attribute from a thing, given 1+ attribute names")
export(types, name='types', doc=""" Namespace containing type aliases from the `types` module,
sans the irritating and lexically unnecessary “Type” suffix --
e.g. `types.ModuleType` can be accessed as just `types.Module`
from this namespace, which is less pointlessly redundant and
aesthetically more pleasant, like definitively.
""")
export(BUILTINS, name='BUILTINS')
export(haspyattr, name='haspyattr')
export(anyattrs, name='anyattrs')
export(allattrs, name='allattrs')
export(anypyattrs, name='anypyattrs')
export(allpyattrs, name='allpyattrs')
export(isiterable, name='isiterable')
export(isabstractmethod, name='isabstractmethod')
export(isabstract, name='isabstract')
export(isabstractcontextmanager, name='isabstractcontextmanager')
export(iscontextmanager, name='iscontextmanager')
export(numeric_types, name='numeric_types')
export(array_types, name='array_types')
export(bytes_types, name='bytes_types')
export(string_types, name='string_types')
export(path_classes, name='path_classes')
export(path_types, name='path_types')
export(file_types, name='file_types')
export(callable_types, name='callable_types')
export(ispathtype, name='ispathtype')
export(ispath, name='ispath')
export(isvalidpath, name='isvalidpath')
export(isnumber, name='isnumber')
export(isnumeric, name='isnumeric')
export(isarray, name='isarray')
export(isstring, name='isstring')
export(isbytes, name='isbytes')
export(isfunction, name='isfunction')
export(export) # hahaaaaa
export(None, name='__exports__') # in name only
export(None, name='__all__') # in name only
__all__ = tuple(__exports__.keys())
__dir__ = lambda: list(__all__)
def test():
from pprint import pprint
SEPARATOR_WIDTH = 80
print_separator = lambda: print('-' * SEPARATOR_WIDTH)
print("ALL: (length=%i)" % len(__all__))
print()
pprint(__all__)
print()
print("DIR(): (length=%i)" % len(__dir__()))
print()
pprint(__dir__())
print()
print("EXPORTS: (length=%i)" % len(__exports__))
print()
pprint(__exports__)
print()
print("» Checking “attr(•) accessor …”")
print()
import plistlib
dump = attr(plistlib, 'dumps', 'writePlistToString')
load = attr(plistlib, 'loads', 'readPlistFromString')
assert dump is not None
assert load is not None
wat = attr(plistlib, 'yo_dogg', 'wtf_hax')
assert wat is None
print("» Checking basic isXXX(•) functions …")
print()
assert graceful_issubclass(int, int)
assert isnumeric(int)
assert isarray(array.array)
assert isstring(str)
assert isstring("")
assert isbytes(bytes)
assert isbytes(bytearray)
assert isbytes(b"")
assert islambda(lambda: None)
assert isfunction(lambda: None)
assert isfunction(export)
# assert not isfunction(SimpleNamespace)
print("» Checking lambda naming …")
lammy = lambda: None
print("» lambda name = %s" % lammy.__name__)
print("» lambda name = %s" % pyattr(lammy, 'name', 'qualname'))
lammy_name = lammy.__name__
lammy_pyattr_name = pyattr(lammy, 'name', 'qualname')
lambda_name = λ
assert lammy_name == lammy_pyattr_name
assert lammy_name == lambda_name
assert lammy_pyattr_name == lambda_name
print()
print("» Checking “types.__doc__ …”")
print()
print_separator()
print(types.__doc__)
print_separator()
print()
dict_one = { 'compress_level' : 9,
'optimize' : True,
'format' : 'png' }
dict_two = { 'yo' : 'dogg' }
dict_three = { 'compress_level' : 10,
'optimize' : True,
'format' : 'jpg' }
merged = merge_all(dict_one, dict_two, dict_three)
print("» Checking “merge_all(•) …”")
print()
assert merged == { 'compress_level' : 9,
'optimize' : True,
'format' : 'png',
'yo' : 'dogg' }
print_separator()
pprint(merged)
print_separator()
print()
if __name__ == '__main__':
test()
# -*- encoding: utf-8 -*-
from replenv import *
if six.PY3:
from replutilities import *
import keyvalue
# -*- encoding: utf-8 -*-
from replenv import *
if six.PY3:
from replutilities import *
import keyvalue
# -*- encoding: utf-8 -*-
from replenv import *
if six.PY3:
from replutilities import *
import keyvalue
@fish2000
Copy link
Author

The banners look like so:

Screen Shot 2019-02-27 at 6 22 54 PM 1

@fish2000
Copy link
Author

The replenv code is now part of Homage: q.v. https://github.com/fish2000/homage/tree/master/.script-bin for the latest updates

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment