easydev-0.9.35/0000775000175000017500000000000013131155451014645 5ustar cokelaercokelaer00000000000000easydev-0.9.35/setup.cfg0000664000175000017500000000056713131155451016476 0ustar cokelaercokelaer00000000000000[sdist] [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 [global] [build_sphinx] source_dir = doc/source build_dir = doc/build all_files = 1 [nosetests] tests = test with-coverage = 1 cover-package = easydev verbosity = 2 logging-level = ERROR attr = !skip,!notravis [upload_docs] upload_dir = doc/build/html/ [tool:pytest] addopts = --durations=10 --verbose easydev-0.9.35/MANIFEST.in0000664000175000017500000000015113047372061016404 0ustar cokelaercokelaer00000000000000include COPYING include README.rst recursive-include easydev/share * include easydev/share/copybutton.js easydev-0.9.35/PKG-INFO0000664000175000017500000000547013131155451015750 0ustar cokelaercokelaer00000000000000Metadata-Version: 1.1 Name: easydev Version: 0.9.35 Summary: Common utilities to ease the development of Python packages Home-page: ['http://packages.python.org/easydev/'] Author: Thomas Cokelaer Author-email: cokelaer@ebi.ac.uk License: new BSD Download-URL: ['http://pypi.python.org/pypi/easydev'] Description: easydev ########## .. image:: https://badge.fury.io/py/easydev.svg :target: https://pypi.python.org/pypi/easydev .. image:: https://secure.travis-ci.org/cokelaer/easydev.png :target: http://travis-ci.org/cokelaer/easydev .. image:: https://coveralls.io/repos/cokelaer/easydev/badge.svg?branch=master :target: https://coveralls.io/r/cokelaer/easydev?branch=master .. image:: https://landscape.io/github/cokelaer/easydev/master/landscape.png :target: https://landscape.io/github/cokelaer/easydev/master :documentation: http://easydev-python.readthedocs.io/en/latest/ :contributions: Please join https://github.com/cokelaer/easydev :source: Please use https://github.com/cokelaer/easydev :issues: Please use https://github.com/cokelaer/easydev/issues :Python version supported: 2.6, 2.7, 3.3, 3.4, 3.5, 3.6 The `easydev `_ package provides miscellaneous functions that are repeatidly used during the development of Python packages. The goal is to help developers on speeding up their own dev. It has been used also as an incubator for other packages (e.g., http://pypi.python.org/pypi/colormap) and is stable. .. warning:: I'm not pretending to provide universal and bug-free tools. The tools provided may also change. However, **easydev** is used in a few other packages such as `bioservices `_, `sequana `_ or `GDSCTools `_ to give a few examples. Keywords: package,multisetup,logging,config,decorators,multigit,progressbar Platform: Linux Platform: Unix Platform: MacOsX Platform: Windows Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Topic :: Software Development :: Libraries :: Python Modules easydev-0.9.35/setup.py0000664000175000017500000000656613130515454016376 0ustar cokelaercokelaer00000000000000# -*- coding: utf-8 -*- __revision__ = "$Id$" import sys import os from setuptools import setup, find_packages import glob _MAJOR = 0 _MINOR = 9 _MICRO = 35 version = '%d.%d.%d' % (_MAJOR, _MINOR, _MICRO) release = '%d.%d' % (_MAJOR, _MINOR) metainfo = { 'authors': {'Cokelaer':('Thomas Cokelaer','cokelaer@ebi.ac.uk')}, 'version': version, 'license' : 'new BSD', 'download_url' : ['http://pypi.python.org/pypi/easydev'], 'url' : ["http://packages.python.org/easydev/"], 'description':'Common utilities to ease the development of Python packages' , 'platforms' : ['Linux', 'Unix', 'MacOsX', 'Windows'], 'keywords' : ["package", "multisetup", "logging", "config", "decorators", "multigit", "progressbar"], 'classifiers' : [ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', 'License :: OSI Approved', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Topic :: Software Development :: Libraries :: Python Modules' ] } setup( name = 'easydev', version = version, maintainer = metainfo['authors']['Cokelaer'][0], maintainer_email = metainfo['authors']['Cokelaer'][1], author = metainfo['authors']['Cokelaer'][0], author_email = metainfo['authors']['Cokelaer'][1], long_description = open("README.rst").read(), keywords = metainfo['keywords'], description = metainfo['description'], license = metainfo['license'], platforms = metainfo['platforms'], url = metainfo['url'], download_url = metainfo['download_url'], classifiers = metainfo['classifiers'], # package installation packages = ['easydev', "easydev.share" ], # using pip, files inside ./easydev/share that are non Python will be # included as well. For a distribution (using setup sdist), the MANIFEST # must be updated accordingly include_package_data = True, package_data = {"easydev.share": [ "themes/standard/*html", "themes/standard/static/*", "themes/cno/*html", "themes/cno/static/*", "copybutton.js"]}, install_requires = ['colorama', 'pexpect'], extras_require = { 'profiler': ["line_profiler_test"] }, # somehow, the google_head.html is found in themes/standard and themese/cno # directories thanks to the contents of datafiles variable but the ones from # themes/standard directory are not copied inside the distribution ? # using the MANIFEST.in solve the issue. However, data_files=datafiles is # still required for python setup.py install or pip install to copy the # share directory in the proper place. sure there will be a neat solution # one day zip_safe = False, entry_points = { 'console_scripts': [ 'easydev_buildPackage=easydev.package:buildPackage', 'browse=easydev.browser:main', ] }, ) easydev-0.9.35/test/0000775000175000017500000000000013131155451015624 5ustar cokelaercokelaer00000000000000easydev-0.9.35/test/test_cnolab_sphinx.py0000664000175000017500000000035213052627511022067 0ustar cokelaercokelaer00000000000000import easydev def test_path(): p = easydev.get_path_sphinx_themes() import os dirs = os.listdir(p) assert 'standard' in dirs def test_sphinx_themes(): p = easydev.get_sphinx_themes() assert 'standard' in p easydev-0.9.35/test/test_misc.py0000664000175000017500000000107413130521312020162 0ustar cokelaercokelaer00000000000000from easydev.misc import get_home, cmd_exists, in_ipynb #from mock import patch import nose.tools import subprocess import os.path def test_get_home(): get_home() #with patch.object(os.path, 'expanduser') as mymock: # mymock.side_effect = ImportError # get_home() def test_cmd_exists(): assert cmd_exists('dummy_dummy') == False assert cmd_exists('ls') == True #with patch.object(subprocess, 'call') as mymock: # mymock.side_effect = Exception # cmd_exists('ls') def test_in_ipynb(): assert in_ipynb() == False easydev-0.9.35/test/test_doc.py0000664000175000017500000000012312655403347020010 0ustar cokelaercokelaer00000000000000from easydev import doc def test_underline(): a = doc.underline("test") easydev-0.9.35/test/test_profiler.py0000664000175000017500000000021013036703751021056 0ustar cokelaercokelaer00000000000000from easydev import do_profile @do_profile() def test_profile(): @do_profile() def test_runme(): a = 1 a**2 easydev-0.9.35/test/test_multicore.py0000664000175000017500000000107212655403347021252 0ustar cokelaercokelaer00000000000000from easydev.multicore import MultiProcessing def func(n=400, *args, **kargs): """A simple test function to play with MultiProcessing class :param n: 400 takes about 5 seconds """ print("inside func", args, kargs) import math for i in range(0,n): for j in range(0,n): for k in range(0,n): math.sqrt(float(i)) d = {'id':n} return d def test_func(): t = MultiProcessing(verbose=True) t.add_job(func, 200) t.add_job(func, 100) t.add_job(func, 50) t.run() print(t.results) easydev-0.9.35/test/test_dependencies.py0000664000175000017500000000022412655403347021673 0ustar cokelaercokelaer00000000000000from easydev.dependencies import get_dependencies def test(): get_dependencies("easydev") get_dependencies("easydev_dummyi_dont_exists") easydev-0.9.35/test/test_copybutton.py0000664000175000017500000000064612655403347021463 0ustar cokelaercokelaer00000000000000from easydev import copybutton def test_copybutton(): copybutton.get_copybutton_path() def test_copy_javascript_into_static_path(): copybutton.copy_javascript_into_static_path("_static", copybutton.get_copybutton_path()) copybutton.copy_javascript_into_static_path("_static", copybutton.get_copybutton_path()) import os os.remove("_static/copybutton.js") os.rmdir("_static") easydev-0.9.35/test/test_console.py0000664000175000017500000000047613052647501020712 0ustar cokelaercokelaer00000000000000from easydev.console import * from easydev import console def test_get_terminal_width(): get_terminal_width() def test_term_width_line(): term_width_line('text') def test_color_terminal(): color_terminal() def test_print_color(): print(purple('\t%s' % "test")) print(red('\t%s' % "test")) easydev-0.9.35/test/test_progressbar.py0000664000175000017500000000113413036702645021574 0ustar cokelaercokelaer00000000000000 from easydev import progressbar import time from easydev import Progress def test_progressbar(): N = 2 p = progressbar.progress_bar(N) for i in range(0,N): time.sleep(.1) p.animate(i+1, i) p = progressbar.TextProgressBar(N, progressbar.consoleprint) for i in range(0,N): time.sleep(.1) p.animate(i+1, i) p = Progress(100) p.animate(1) assert p.pb.interval == 1 p = Progress(200) assert p.pb.interval == 2 p.animate(1) # IPYthon test ? fails on travis # p = progressbar.IPythonNotebookPB(200) # p.animate(1) easydev-0.9.35/test/test_timer.py0000664000175000017500000000026213036717151020362 0ustar cokelaercokelaer00000000000000from easydev.timer import Timer import time def test_timer(): times = [] with Timer(times): time.sleep(.1) assert len(times) == 1 assert sum(times) <1 easydev-0.9.35/test/test_platform.py0000664000175000017500000000150713130520603021056 0ustar cokelaercokelaer00000000000000import easydev from easydev.platform import get_platform, linux_distribution, is_windows from easydev.platform import is_linux, is_mac def test_platform(mocker): assert get_platform() in ['Linux', 'Windows', 'Mac'] linux_distribution() is_windows() is_linux() is_mac() """def func(): raise Exception with patch("platform.linux_distribution", func): get_platform() """ mocker.patch.object(easydev.platform, "get_platform") easydev.platform.get_platform.return_value = "Windows" assert is_linux() is False assert is_mac() is False assert is_windows() is True mocker.patch.object(easydev.platform, "get_platform") easydev.platform.get_platform.return_value = "Mac" assert is_linux() is False assert is_windows() is False assert is_mac() is True easydev-0.9.35/test/test_appdirs.py0000664000175000017500000000117313052627642020711 0ustar cokelaercokelaer00000000000000import mock def test_app(): def getter(app): app.user_data_dir app.site_data_dir app.user_config_dir app.site_config_dir app.user_cache_dir app.user_log_dir with mock.patch("sys.platform", "darwin"): from easydev import appdirs app = appdirs.AppDirs("test") getter(app) with mock.patch("sys.platform", "win32"): from easydev import appdirs app = appdirs.AppDirs("test") getter(app) with mock.patch("sys.platform", "linux"): from easydev import appdirs app = appdirs.AppDirs("test") getter(app) easydev-0.9.35/test/test_package.py0000664000175000017500000000051512655403347020643 0ustar cokelaercokelaer00000000000000from easydev import PackageBuilder def test_package(): p = PackageBuilder("tstPkg") p.logging.debugLevel = "ERROR" p.buildPackage() p.buildPackage(force=True) import shutil shutil.rmtree("tstPkg") def test_options(): from easydev import package package.OptionsBuildPackage(["--pkgname", "test"]) easydev-0.9.35/test/test_logging_tools.py0000664000175000017500000000071013052637317022111 0ustar cokelaercokelaer00000000000000from easydev import Logging def test_logging(): l = Logging("INFO") l.info("test") l.level = "WARNING" l.level == "INFO" l.level == "CRITICAL" l.level == "ERROR" l.level == "DEBUG" l.level = True l.level = False try: l.level = "WARN" assert Fales except: assert True # FIXME is this working ??wierd syntax in loggibg_tools. import copy copy.copy(l) copy.deepcopy(l) easydev-0.9.35/test/test_md5.py0000664000175000017500000000037613002701112017714 0ustar cokelaercokelaer00000000000000from easydev.md5tools import md5 from easydev import TempFile def test_md5(): with TempFile() as temp: fh = open(temp.name, "w") fh.write("youpi") fh.close() assert md5(fh.name) == "538e957924f0770b415f473ce900d686" easydev-0.9.35/test/test_url.py0000664000175000017500000000067013052634326020047 0ustar cokelaercokelaer00000000000000from easydev import isurl_reachable from nose.plugins.attrib import attr def test_isurl(): assert isurl_reachable("www.google.com") == True assert isurl_reachable("http://www.google.com") == True assert isurl_reachable("https://fr.yahoo.com") == False # moved assert isurl_reachable("wrong.co.ujj") == False assert isurl_reachable("http://wrong.co.ujj") == False assert isurl_reachable("http://wrong.co") == False easydev-0.9.35/test/test_config.py0000664000175000017500000000540213052644054020507 0ustar cokelaercokelaer00000000000000from easydev.config_tools import ConfigExample, DynamicConfigParser, load_configfile from easydev import CustomConfig import os def test_config_custom(): c = CustomConfig('dummy') c.init() c.user_config_dir c.remove() def test_configExample(): c = ConfigExample().config assert 'General' in c.sections() assert 'GA' in c.sections() print(c) c.remove_section('General') def test_ordered_dict_attribute(): # disabled because incompatible with python 3 c = DynamicConfigParser() c.add_section("GA") c.GA.test = 2 # this is an attribute only, not yet a key c.add_option("GA", "test", 1) c.GA.test = 2 c['GA']['test'] = 4 del c['GA']['test'] def test_DynamicConfig(): c = ConfigExample() dc = DynamicConfigParser(c.config) dc.save('test.ini') dc = DynamicConfigParser('test.ini') #dc = DynamicConfigParser(c.config) dc._replace_config(c.config) dc.GA dc.add_option("GA", "bool", 'True') dc.get_options("GA") os.remove('test.ini') dc.remove_section('GA') assert 'GA' not in dc.sections() print(dc) # try something stupid try: dc = DynamicConfigParser(234) assert False except TypeError: assert True dc = DynamicConfigParser() try: dc.read("test_dummy") assert False except: assert True def test_DynamicConfigDelete(): from easydev import ConfigExample dcp = DynamicConfigParser(ConfigExample().config) try: del(dcp["GA"]) assert dcp.sections() == ['General'] except: pass def test_DynamicConfig_setter(): dc = DynamicConfigParser() dc.add_section("GA") dc.add_option("GA", "test", 1) dc.save("test.ini") dc2 = DynamicConfigParser("test.ini") assert dc == dc2 dc2.GA.test == 1 dc.GA.test = 10 dc.save("test.ini") dc2 = DynamicConfigParser("test.ini") assert dc == dc2 assert dc2.GA.test == '10' os.remove('test.ini') def test_section2dict(): dc = DynamicConfigParser() dc.add_section("GA") dc.add_option("GA", "test", 1) def test_compare(): dc = DynamicConfigParser() dc.add_section("GA") dc.add_option("GA", "test", 1) dc2 = DynamicConfigParser() dc2.add_section("GA2") dc2.add_option("GA2", "test", 1) assert (dc==dc2) == False dc = DynamicConfigParser() dc.add_section("GA") dc.add_option("GA", "test", 1) dc2 = DynamicConfigParser() dc2.add_section("GA") dc2.add_option("GA", "test", 2) assert (dc==dc2) == False dc = DynamicConfigParser() dc.add_section("GA") dc.add_option("GA", "test", 1) dc2 = DynamicConfigParser() dc2.add_section("GA") dc2.add_option("GA", "test", 1) assert (dc==dc2) == True easydev-0.9.35/test/test_browse.py0000664000175000017500000000131413052652162020540 0ustar cokelaercokelaer00000000000000from easydev import browser try: from unittest.mock import patch except: from mock import patch def test_browse(mocker): def func(*args, **kwargs): pass with patch('webbrowser.open', func): with patch('webbrowser.open_new', func): browser.browse("http://pypi.python.org", verbose=True) browser.browse("pypi.python.org", verbose=True) browser.browse(".", verbose=True) def test_browse_module(mocker): from easydev.browser import main def func(*args, **kwargs): pass with patch('webbrowser.open', func): main(["browse", "--help"]) main(["browse", "." ]) main(["browse", "http://www.uniprot.org" ]) easydev-0.9.35/test/test_paths.py0000664000175000017500000000123312701452245020356 0ustar cokelaercokelaer00000000000000from easydev.paths import * import os def test_get_share_directory_path(): get_shared_directory_path("easydev") try: get_shared_directory_path("dummydummy") assert False except: assert True def test_get_share_directories(): a = get_shared_directories("easydev", "themes") def test_get_share_file(): f = get_share_file("easydev", os.sep.join(["themes", "standard"]), "theme.conf") def test_gsf(): f = gsf("easydev", os.sep.join(["themes", "standard"]), "theme.conf") try: f = gsf("easydev", os.sep.join(["themes", "standard"]), "theme.conf.dummy") assert False except: assert True easydev-0.9.35/test/test_chunks.py0000664000175000017500000000036412715312360020534 0ustar cokelaercokelaer00000000000000from easydev import split_into_chunks def test_chunks(): assert list(split_into_chunks([0,1,2,3,4,5,6], 2)) == [[0, 2, 4, 6], [1, 3, 5]] assert list(split_into_chunks([0,1,2,3,4,5], 2)) == [[0, 2, 4], [1, 3, 5]] test_chunks() easydev-0.9.35/test/test_options.py0000664000175000017500000000152413032202235020723 0ustar cokelaercokelaer00000000000000from easydev import SmartFormatter import argparse class Options(argparse.ArgumentParser): def __init__(self, prog="test"): usage = """standalone test""" description="""blabla""" super(Options, self).__init__(usage=usage, prog=prog, description=description, formatter_class=SmartFormatter) self.add_argument("--config-params", dest="config_params", type=str, help="""FORMAT|Overwrite any field in the config file by using the following convention. A config file is in YAML format and has a hierarchy of parametesr. For example: samples: file1: R1.fastq.gz file2: R2.fastq.gz bwa_mem_phix: mem: threads: 2 """) def test(): options = Options() try: options.parse_args(["--help"]) except: pass easydev-0.9.35/test/test_decorators.py0000664000175000017500000000143213036703471021407 0ustar cokelaercokelaer00000000000000from easydev.decorators import requires, ifpandas, ifpylab, _require class A(object): def __init__(self): pass def create(self): self.a = 1 self.b = 1 @requires("a", "what to do") def print_str(self): print(self.a) @requires(["a",'b'], "what to do") def print_list(self): print(self.a +self.b) @_require("a", "what to do") def print_list(self): print(self.a) def test(): a = A() try: a.print_str() assert False except: assert True try: a.print_list() assert False except: assert True a.create() a.print_str() a.print_list() @ifpandas def test_deco_pandas(): print(1) @ifpylab def test_deco_pylab(): print(1) easydev-0.9.35/test/test_multisetup.py0000664000175000017500000000431712655403347021467 0ustar cokelaercokelaer00000000000000"""Test make_develop for Multisetup object""" import os from easydev.multisetup import Multisetup from nose import with_setup """!!For the following test, don't use intrusive commands such as install together with run method. !!!!""" curdir = '..' + os.sep + '..' packages = ['easydev'] def test_init(): """ Test initialization of Multisetup object """ mysetup = Multisetup(curdir=curdir, commands='build', packages=packages) assert mysetup.commands == ['build'] assert len(mysetup.packages) == 1 assert mysetup.packages == packages def _test_wrong_package(): mysetup = Multisetup(curdir=curdir, commands=['build', '--packages','corezzz'], packages=['corezzz']) try: mysetup.run() assert False except: assert True def test_parse_packages(): """ Test of parse_packages() method with option --packages""" mysetup = Multisetup(curdir=curdir, commands='dummy', packages=packages) mysetup.commands = ['build', '--packages', 'easydev'] mysetup.parse_packages() assert mysetup.packages == set(['easydev']) def test_parse_no_packages(): """ Test of parse_packages() method with option --exclude-package""" mysetup = Multisetup(curdir=curdir, commands='commands --exclude-packages easydev', packages=packages) print(mysetup.packages) assert mysetup.packages == [] def test_parse_commands(): """ Test of parse_commands() method""" commands =['install', 'sdist', '-d', './dist', '--quiet', '--keep-going'] mysetup = Multisetup(curdir=curdir, commands=commands, packages=[]) mysetup.parse_commands() mysetup.parse_packages() assert len(mysetup.commands) == 2 assert mysetup.commands[0] == 'install' assert mysetup.commands[1] == 'sdist -d ./dist' assert mysetup.verbose == False assert mysetup.force == True def test_setup_failure(): """ Test of run() method with bad option""" commands = '--packages easydev sdist --bad-option' mysetup = Multisetup(curdir=curdir, commands=commands, packages=['easydev']) try: mysetup.run() assert False except: assert True easydev-0.9.35/test/test_codecs.py0000664000175000017500000000126613002676246020511 0ustar cokelaercokelaer00000000000000from easydev import codecs def test_tolist(): assert codecs.tolist(1) == [1] assert codecs.tolist(1.) == [1.] assert codecs.tolist('1') == ['1'] assert codecs.tolist([1]) == [1] assert codecs.tolist([1, 2]) == [1, 2] try: import numpy as np x = np.array([1, 2]) assert codecs.tolist(x) == [1, 2] except: pass assert sorted(codecs.tolist((1, 2))) == [1, 2] codecs.tolist(set([1])) def test_tostring(): assert codecs.list2string([1, 2]) == "1, 2" assert codecs.list2string([1, 2], sep=";") == "1; 2" assert codecs.list2string([1, 2], sep=';', space=False) == "1;2" assert codecs.list2string(1) == "1" easydev-0.9.35/test/test_tools.py0000664000175000017500000000677113036702172020412 0ustar cokelaercokelaer00000000000000from easydev import tools, TempFile def test_check_range(): tools.check_range(1, 0,1) tools.check_range(0, 0,1) tools.check_range(0.5, 0,1) try: tools.check_range(1, 0,1, strict=True) assert False except: assert True try: tools.check_range(0, 0,1, strict=True) assert False except: assert True try: tools.check_range(10, 0,1, strict=False) assert False except: assert True try: tools.check_range(-10, 0,1, strict=False) assert False except: assert True def test_swapdict(): assert {1:'a'} == tools.swapdict({'a':1}) # if the are non-unique values, we can catch the error or no: try: tools.swapdict({'a':1, 'b':1}) assert False except: assert True tools.swapdict({'a':1, 'b':1}, check_ambiguity=False) def test_tools(): tools.shellcmd('ls') tools.shellcmd('ls', show=False) tools.shellcmd('ls', show=True) output = tools.shellcmd('ls', verbose=True) tools.shellcmd('lssssssss', verbose=True, ignore_errors=True) tools.execute('ls') def test_tools2(): try: tools.shellcmd('lsss', verbose=False) assert False except: assert True def test_checkParams(): tools.checkParam(1, [1, 2]) try: tools.checkParam(0, [1, 2]) assert False except: assert True try: tools.checkParam(0, 0) assert False except TypeError: assert True def test_check_param_in_list(): tools.check_param_in_list(1, [0,1,5], "test") try: tools.check_param_in_list(10, [0,1,5]) assert False except: assert True try: tools.check_param_in_list(10, [0,1,5], 'testt') assert False except: assert True def test_precision(): assert tools.precision(2.123) == 2.12 assert tools.precision(2.123, 1) == 2.1 assert tools.precision(2.123,3) == 2.123 assert tools.precision(2123,-2) == 2100 def test_attrdict(): a = tools.AttrDict(value=1) assert a.value == 1 assert 'value' in list(a.keys()) assert 1 in (a.values()) a.description = 'test' assert a['description'] == 'test' a['output'] = 'txt' assert a.output == 'txt' d = {'a':{'b':1}, 'aa':2} ad = tools.AttrDict(**d) assert ad.a.b == 1 ad.a.b = 2 assert ad.a.b == 2 ad['d'] = 4 assert ad.d == 4 try: ad.update(1) assert False except: assert True # check json capabilities fh = TempFile() js = ad.to_json() ad.to_json(filename=fh.name) ad.from_json(fh.name) fh.delete() def test_devtools(): d = tools.DevTools() d.check_param_in_list(1, [1,2]) d.check_range(1,0,2) assert d.list2string(['a', 'b']) == 'a,b' assert d.swapdict({'a':1}) == {1:'a'} d.to_json({'a':1}) assert d.to_list('a') == ['a'] import tempfile, os d.mkdirs(tempfile.mkdtemp() + os.sep + "test") try: d.check_exists("ttttttttttt") assert False except: assert True d.mkdir(tempfile.mkdtemp()) def test_mkdirs(): import tempfile, os tools.mkdirs(tempfile.mkdtemp() + os.sep + "test") try: tools.mkdirs(tempfile.mkdtemp() + os.sep + "test") assert False except: assert True # without / , was not working but is now part of the API tools.mkdirs(tempfile.mkdtemp()) def test_touch(): with TempFile() as fh: fh.name tools.touch(fh.name) easydev-0.9.35/test/test_easytest.py0000664000175000017500000000102113052637302021072 0ustar cokelaercokelaer00000000000000from easydev.easytest import * class A(object): pass def test_trysetattr(): trysetattr(A, "test", 1, possible=True) try: trysetattr(A, "test", 1, possible=False) assert False except: assert True def test_tempfile(): f = TempFile() f.name f.delete() with TempFile() as fh: pass def test_list_almost_equal(): assert_list_almost_equal([1,2],[1,2]) try: assert_list_almost_equal([1,2],[1,3]) assert False except: assert True easydev-0.9.35/README.rst0000664000175000017500000000302013052645332016333 0ustar cokelaercokelaer00000000000000easydev ########## .. image:: https://badge.fury.io/py/easydev.svg :target: https://pypi.python.org/pypi/easydev .. image:: https://secure.travis-ci.org/cokelaer/easydev.png :target: http://travis-ci.org/cokelaer/easydev .. image:: https://coveralls.io/repos/cokelaer/easydev/badge.svg?branch=master :target: https://coveralls.io/r/cokelaer/easydev?branch=master .. image:: https://landscape.io/github/cokelaer/easydev/master/landscape.png :target: https://landscape.io/github/cokelaer/easydev/master :documentation: http://easydev-python.readthedocs.io/en/latest/ :contributions: Please join https://github.com/cokelaer/easydev :source: Please use https://github.com/cokelaer/easydev :issues: Please use https://github.com/cokelaer/easydev/issues :Python version supported: 2.6, 2.7, 3.3, 3.4, 3.5, 3.6 The `easydev `_ package provides miscellaneous functions that are repeatidly used during the development of Python packages. The goal is to help developers on speeding up their own dev. It has been used also as an incubator for other packages (e.g., http://pypi.python.org/pypi/colormap) and is stable. .. warning:: I'm not pretending to provide universal and bug-free tools. The tools provided may also change. However, **easydev** is used in a few other packages such as `bioservices `_, `sequana `_ or `GDSCTools `_ to give a few examples. easydev-0.9.35/COPYING0000664000175000017500000000277713047373327015727 0ustar cokelaercokelaer00000000000000New BSD License Copyright (c) Thomas Cokelaer All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c. Neither the name of the Scikit-learn Developers nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. easydev-0.9.35/easydev/0000775000175000017500000000000013131155451016305 5ustar cokelaercokelaer00000000000000easydev-0.9.35/easydev/platform.py0000664000175000017500000000520013130521417020476 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## from __future__ import absolute_import # avoids conflict with standard module import os import sys import platform as plf __all__ = ["get_platform", "linux_distribution", ""] def linux_distribution(): try: # Note that this module has the same name as the standard module # hence the renaming of the standard module here below return plf.linux_distribution() except Exception as err: print(err) return "unknown" def get_platform(): """Identify the platform (Linux/windows/Mac) The folloing modules/functions can be used to identify the platform: platform, sys.platform, os.name, os.environ. We use platform and return the content of platform.system. If it does not work, sys.platform is used and sys.platform output interpreted: linux, java, win and darwin are searched for and returned aas Linux, Java, Windows, Darwin to be consistent with the output of platform.syste. If those strings are not found, just return the output of sys.platform. :return: 'Linux' or 'Windows' or 'Darwin', 'Java' if platform can be determined otherwise, the content of sys.platform() """ try: platform = plf.system() return platform except: # platform is not available under all systems (e.g., WLST tool # see http://stackoverflow.com/questions/1854/python-what-os-am-i-running-on # so, let us try sys.platform platform = sys.platform if platform.startswith('linux'): platform = 'Linux' elif platform.startswith('java'): platform = 'Java' elif platform.startswith('win'): platform = 'Windows' elif platform.startswith('darwin'): platform = 'Darwin' else: print("platform not parsed. Return raw value of sys.platform.") return platform def is_windows(): if get_platform() == 'Windows': return True else: return False def is_linux(): if get_platform() == 'Linux': return True else: return False def is_mac(): if get_platform() == 'Mac': return True else: return False easydev-0.9.35/easydev/multicore.py0000664000175000017500000001064313036730537020676 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## import time from multiprocessing import cpu_count, Process, Queue, Pool __all__ = ["MultiProcessing"] class MultiProcessing(object): """Class to run jobs in an asynchronous manner. You would use this class to run several jobs on a local computer that has several cpus. :: t = MultiProcessing(maxcpu=2) t.add_job(func, func_args) t.run() t.results[0] # contain returned object from the function *func*. .. warning:: the function must be a function, not a method. This is inherent to multiprocess in the multiprocessing module. .. warning:: the order in the results list may not be the same as the list of jobs. see :meth:`run` for details """ def __init__(self, maxcpu=None, verbose=False, progress=True): """ :param maxcpu: default returned by multiprocessing.cpu_count() :param verbose: print the output of each job. Could be very verbose so we advice to keep it False. :param progress: shows the progress """ if maxcpu == None: maxcpu = cpu_count() self.maxcpu = maxcpu self.reset() self.verbose = verbose self.progress = progress def reset(self): """remove joves and results""" self.jobs = [] # a list of processes self.results = Queue() # the results to append def add_job(self, func, *args, **kargs): """add a job in the pool""" if self.verbose: print("Adding jobs in the queue..",) t = Process(target=func, args=args, kwargs=kargs) self.jobs.append(t) def _cb(self, results): if self.verbose is True: print("callback", results) if self.progress is True: self.pb.animate(len(self.results)+1) self.results.append(results) def run(self, delay=0.1, verbose=True): """Run all the jobs in the Pool until all have finished. Jobs that have been added to the job list in :meth:`add_job` are now processed in this method by using a Pool. Here, we add all jobs using the apply_async method from multiprocess module. In order to ensure that the jobs are run sequentially in the same order as in :attr:`jobs`, we introduce a delay between 2 calls to apply_async (see http://docs.python.org/2/library/multiprocessing.html) A better way may be t use a Manager but for now, this works. """ from easydev import Progress if self.progress is True: self.pb = Progress(len(self.jobs), 1) self.pb.animate(0) def init_worker(): import signal signal.signal(signal.SIGINT, signal.SIG_IGN) self.results = [] self.pool = Pool(self.maxcpu, init_worker) for process in self.jobs: self.pool.apply_async(process._target, process._args, process._kwargs, callback=self._cb) # ensure the results have same order as jobs # maybe important if you expect the order of the results to # be the same as inut; otherwise set delay to 0 time.sleep(delay) try: while True: time.sleep(1) # check if all processes are finished. # if so, finished. count = len(self.results) if count == len(self.jobs): break except KeyboardInterrupt: print("\nCaught interruption. " + "Terminating the Pool of processes... ",) self.pool.terminate() self.pool.join() print("... done") else: # Closing properly the pool self.pool.close() self.pool.join() # Pool cannot be pickled. So, if we want to pickel "MultiProcessing" # class itself, we must desctroy this instance del self.pool self.finished = True easydev-0.9.35/easydev/profiler.py0000664000175000017500000000371213052634677020522 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## """ Usage:: from easydev import do_profile @do_profile() def test(): x = 1 x *= x return x # source: https://zapier.com/engineering/profiling-python-boss/ Requires line_profiler to be installed. line_profiler has C code and we wish easydev to be as simple as possible. So, we do not want compiled code dependencies. Consequently, we added line_profiler in the extra_requires instead of requires in the setup.py One must install line_profiler itself. """ __all__ = ["do_profile"] try: from line_profiler import LineProfiler def do_profile(follow=[]): def inner(func): def profiled_func(*args, **kwargs): try: profiler = LineProfiler() profiler.add_function(func) for f in follow: profiler.add_function(f) profiler.enable_by_count() return func(*args, **kwargs) finally: profiler.print_stats() return profiled_func return inner except ImportError: def do_profile(follow=[]): "Helpful if you accidentally leave in production!" print("easydev warning:: line_profiler does not seem to be installed. " + "Type 'pip install line_profiler'") def inner(func): def nothing(*args, **kwargs): return func(*args, **kwargs) return nothing return inner easydev-0.9.35/easydev/misc.py0000664000175000017500000000406113130521322017604 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## import os __all__ = ['get_home', 'cmd_exists'] def get_home(): # This function should be robust # First, let us try with expanduser try: homedir = os.path.expanduser("~") except ImportError: # This may happen. pass else: if os.path.isdir(homedir): return homedir # Then, with getenv for this in ('HOME', 'USERPROFILE', 'TMP'): # getenv is same as os.environ.get homedir = os.environ.get(this) if homedir is not None and os.path.isdir(homedir): return homedir def cmd_exists(cmd): try: import subprocess # for unix/max only result = subprocess.call("type " + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if result == 0: return True else: return False except Exception: # If subprocess is not found, we assume it exists. # This choice ensure that if it fails, we keep going. return True def in_ipynb(): """Checks if we are in an ipython notebook :return: True if in an ipython notebook otherwise returns False """ try: cfg = get_ipython().config if 'parent_appname' in cfg['IPKernelApp'].keys() and \ cfg['IPKernelApp']['parent_appname'] == 'ipython-notebook': return True elif "connection_file" in cfg['IPKernelApp'].keys(): if "jupyter" in cfg['IPKernelApp']['connection_file']: return True return False except NameError: return False easydev-0.9.35/easydev/decorators.py0000664000175000017500000001215113036730473021033 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2012-2014 - # # File author(s): Thomas Cokelaer (cokelaer, gmail.com) # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # website: http://github.com/cokelaer/easydev # ############################################################################## """Handy decorators""" from functools import wraps import threading __all__ = ['ifpylab', 'requires', 'ifpandas'] # decorator with arguments and optional arguments for a method def _require(*args_deco, **kwds_deco): """Decorator for class method to check if an attribute exist .. doctest:: from easydev.decorators import require class Test(object): def __init__(self): self.m = 1 @require('m', "set the m attribute first") def print(self): print self.m t = Test() t.print() .. todo:: first argument could be a list """ if len(args_deco) != 2: raise ValueError("require decorator expects 2 parameter. First one is" + "the required attribute. Second one is an error message.") attribute = args_deco[0] msg = args_deco[1] if len(attribute.split('.')) > 2: raise AttributeError('This version of require decorator introspect only 2 levels') def decorator(func): # func: function object of decorated method; has # useful info like f.func_name for the name of # the decorated method. def newf(*args, **kwds): # This code will be executed in lieu of the # method you've decorated. You can call the # decorated method via f(_args, _kwds). names = attribute.split('.') if len(names) == 1: if hasattr(args[0], attribute): return func(*args, **kwds) else: raise AttributeError('%s not found. %s' % (names, msg)) elif len(names) == 2: if hasattr(getattr(args[0], names[0]), names[1]): return func(*args, **kwds) else: raise AttributeError('%s not found. %s' % (names, msg)) newf.__name__ = func.__name__ newf.__doc__ = func.__doc__ return newf return decorator # for book keeping, could be useful: """ def _blocking(not_avail): def blocking(f, *args, **kw): if not hasattr(f, "thread"): # no thread running def set_result(): f.result = f(*args, **kw) f.thread = threading.Thread(None, set_result) f.thread.start() return not_avail elif f.thread.isAlive(): return not_avail else: # the thread is ended, return the stored result del f.thread return f.result return blocking """ # same as require decorator but works with list of stirngs of # single string and uses the functools utilities def requires(requires, msg=""): """Decorator for class method to check if an attribute exist .. doctest:: >>> from easydev.decorators import requires >>> class Test(object): ... def __init__(self): ... self.m = 1 ... self.x = 1 ... @requires(['m','x'], "set the m attribute first") ... def printthis(self): ... print(self.m+self.x) >>> t = Test() >>> t.printthis() 2 """ if isinstance(requires, str): requires = [requires] elif isinstance(requires, list) == False: raise TypeError("First argument of the /requires/ decorator must be a" + "string or list of string representing the required attributes" + "to be found in your class. Second argument is a " + "complementary message. ") def actualDecorator(f): @wraps(f) def wrapper(*args, **kwds): for require in requires: if hasattr(args[0], require) == False: raise AttributeError("{} not found in {}. ".format(require, args[0]) + msg) return f(*args, **kwds) return wrapper return actualDecorator # could be a macro maybe def ifpandas(func): """check if pandas is available. If so, just return the function, otherwise returns dumming function that does nothing """ def wrapper(*args, **kwds): return func(*args, **kwds) try: import pandas return wrapper except Exception: def dummy(): pass return dummy def ifpylab(func): """check if pylab is available. If so, just return the function, otherwise returns dumming function that does nothing """ # for functions def wrapper(*args, **kwds): return func(*args, **kwds) # for methods try: import pylab return wrapper except Exception: def dummy(): pass return dummy easydev-0.9.35/easydev/tools.py0000664000175000017500000002412013056023406020016 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## """toolkit to ease development""" import subprocess import json import os import sys __all__ = ["shellcmd", "swapdict", "check_param_in_list", "check_range", "precision", "AttrDict", "DevTools", "execute", "touch", "mkdirs"] def precision(data, digit=2): """Round values in a list keeping only N digits precision :: >>> precision(2.123) 2.12 >>> precision(2123, digit=-2) 2100 """ data = int(data*pow(10, digit)) data /= pow(10., digit) return data def check_range(value, a, b, strict=False): """Check that a value lies in a given range :param value: value to test :param a: lower bound :param b: upper bound :return: nothing .. doctest:: >>> from easydev.tools import check_range >>> check_range(1,0, 2) """ if strict is True: if value <= a: raise ValueError(" {} must be greater (or equal) than {}".format(value, a)) if value >= b: raise ValueError(" {} must be less (or less) than {}".format(value, b)) elif strict is False: if value < a: raise ValueError(" {} must be greater than {}".format(value, a)) if value > b: raise ValueError(" {} must be less than {}".format(value, b)) def checkParam(param, valid_values): """ .. warning:: deprecated since 0.6.10 use :meth:`check_param_in_list` instead """ print("easydev WARNING:: deprecated; use check_param_in_list instead.") check_param_in_list(param, valid_values) def check_param_in_list(param, valid_values, name=None): """Checks that the value of param is amongst valid :param param: a parameter to be checked :param list valid_values: a list of values :: check_param_in_list(1, [1,2,3]) check_param_in_list(mode, ["on", "off"]) """ if isinstance(valid_values, list) is False: raise TypeError("the valid_values second argument must be a list of valid values. {0} was provided.".format(valid_values)) if param not in valid_values: if name: msg = "Incorrect value provided for {} ({})".format(name, param) else: msg = "Incorrect value provided (%s)" % param msg += " Correct values are %s" % valid_values raise ValueError(msg) def shellcmd(cmd, show=False, verbose=False, ignore_errors=False): """An alias to run system commands with Popen. Based on subprocess.Popen. :param str cmd: the command to call :param bool show: print the command :param bool verbose: print the output :return: the output as a string """ if show: print(cmd) try: ret = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) output = ret.stdout.read().strip() error = ret.stderr.read().strip() ret.wait() if len(error) > 0: if ignore_errors is False: raise Exception(error) else: if verbose is True: print("Errors/Warning" + str(error)) if verbose is True: print(output) return output except Exception as err: raise Exception("Error:: Command (%s) failed. Error message is %s" % (cmd, err)) def execute(cmd, showcmd=True, verbose=True): """An alias to run system commands using pexpect. :param cmd: :param showcmd: :param verbose: """ import pexpect if showcmd is True: print(cmd) p = pexpect.spawn(cmd, timeout=None) line = p.readline() while line: if verbose: try: sys.stdout.write(line.decode()) except: sys.stdout.write(line) sys.stdout.flush() line = p.readline() def touch(fname, times=None): """Touch a file (like unix command) """ with open(fname, 'a'): os.utime(fname, times) def swapdict(dic, check_ambiguity=True): """Swap keys for values in a dictionary :: >>> d = {'a':1} >>> swapdict(d) {1:'a'} """ # this version is more elegant but slightly slower : return {v:k for k,v in dic.items()} if check_ambiguity: assert len(set(dic.keys())) == len(set(dic.values())), "values is not a set. ambiguities for keys." return dict(zip(dic.values(), dic.keys())) def mkdirs(newdir, mode=0o777): """Recursive creation of a directory :source: matplotlib mkdirs. In addition, handles "path" without slashes make directory *newdir* recursively, and set *mode*. Equivalent to :: > mkdir -p NEWDIR > chmod MODE NEWDIR """ # mkdirs("analysis") # without / at the end led to an error # since os.path.split returns ('', 'analysis') try: if not os.path.exists(newdir): parts = os.path.split(newdir) for i in range(1, len(parts) + 1): thispart = os.path.join(*parts[:i]) # if no sep at the end, thispart may be an empty string # so, we need to check if thispart exists and is not of len 0 if not os.path.exists(thispart) and len(thispart): os.makedirs(thispart, mode) except OSError as err: import errno # Reraise the error unless it's about an already existing directory if err.errno != errno.EEXIST or not os.path.isdir(newdir): raise class AttrDict(dict): """dictionary-like object that exposes its keys as attributes. When you have dictionary of dictionaries with many levels e.g.:: d = {'a': {'a1': {'a2': 2}}} to get/set a values, one has to type something like:: d['a']['a1']['a2'] = 3 The :class:`AttrDict` allows the dictionary to work as attributes:: ad = AttrDict(**d) ad.a.a1.a2 = 3 You can now add values as attribute, or with ['key'] syntax .. doctest:: >>> from easydev import AttrDict >>> a = AttrDict(**{'value': 1}) >>> a.value 1 >>> >>> a.unit = 'meter' >>> sorted(a.keys()) ['unit', 'value'] If you need to add new simple values after the creation of the instance, just use the setter:: >>> d['newa'] = 2 >>> d.newa = 2 # equivalent to the statement above but if you want to set a dictionary (whichever recursive level), use the :meth:`update` method:: >>> d.update({'newd': {'g': {'h':2}}}) >>> d.newd.g.h 2 Note that if you use the setter for a value that is a dictionary, e.g.:: ad.a = {'b':1} then *a* is indeed a dictionary. """ def __init__(self, **kwargs): dict.__init__(self, kwargs) self.__dict__ = self self.update(kwargs) def update(self, content): """See class/constructor documentation for details :param dict content: a valid dictionary """ # accepts dict and attrdict classes try: from collections import OrderedDict except: OrderedDict = AttrDict if content.__class__ not in [dict, OrderedDict, AttrDict]: raise TypeError for k, v in content.items(): if v.__class__ not in [dict, AttrDict, OrderedDict]: # fixme copy ? self[k] = v else: self[k] = AttrDict(**v) def from_json(self, filename): """ does not remove existing keys put replace them if already present """ res = json.load(open(filename, "r")) for k,v in res.items(): self[k] = v def to_json(self, filename=None): import json if filename is not None: with open(filename, "w") as fout: json.dump(self, fout) else: return json.dumps(self) class DevTools(object): """Aggregate of easydev.tools functions. """ def check_range(self, value, a, b): """wrapper around :func:`easydev.check_range`""" check_range(value, a, b, strict=False) def check_param_in_list(self, param, valid_values): """wrapper around :func:`easydev.check_param_in_list`""" param = self.to_list(param) for name in param: check_param_in_list(name, list(valid_values)) def swapdict(self, d): """wrapper around :func:`easydev.swapdict`""" return swapdict(d) def to_list(self, query): """Cast to a list if possible 'a' ->['a'] 1 -> [1] """ from easydev import codecs return codecs.to_list(query) def list2string(self, query, sep=",", space=False): """ see :func:`easydev.tools.list2string` """ from easydev import codecs return codecs.list2string(query, sep=sep, space=space) def to_json(self, dictionary): """Transform a dictionary to a json object""" return json.dumps(dictionary) def mkdir(self, dirname): """Create a directory if it does not exists; pass without error otherwise""" try: os.mkdir(dirname) except OSError: pass # exists already except Exception as err: raise(err) def shellcmd(self, cmd, show=False, verbose=False, ignore_errors=False): """See :func:`shellcmd`""" return shellcmd(cmd, show=show, verbose=verbose, ignore_errors=ignore_errors) def check_exists(self, filename): """Raise error message if the file does not exists""" if os.path.exists(filename) is False: raise ValueError("This file %s does not exists" % filename) def mkdirs(self, dirname, mode=0o777): mkdirs(dirname, mode=mode) easydev-0.9.35/easydev/md5tools.py0000664000175000017500000000150613036730522020431 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## """md5 utility""" import hashlib def md5(fname, chunk=65536): """Return the MD5 checksums of a file Takes about 25 seconds on a 8Gb file. """ hash_md5 = hashlib.md5() with open(fname, "rb") as f: for this in iter(lambda: f.read(chunk), b""): hash_md5.update(this) return hash_md5.hexdigest() easydev-0.9.35/easydev/multisetup.py0000664000175000017500000003125213036730544021103 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## """Calling setup.py recursively and/or in multi python packages. The commands are similar to those expected by setup.py. In addition, there are a few commands dedicated to multisetup (see --help). :Example: .. doctest:: :options: +SKIP >>> python multisetup install --quiet >>> python multisetup install sdist --dist-dir ../dist >>> python multisetup --keep-going install sdist --dist-dir ../dist Based on OpenAlea.Misc http://openalea.gforge.inria.fr """ __license__ = "GPLv3" __revision__ = "$Id$" import sys import os from subprocess import PIPE, Popen try: from easydev.console import bold, red, green, \ color_terminal, nocolor, underline, purple except ImportError: pass """ args = sys.argv[1:] if len(args) == 1 and args[0] in ['-h', '--help']: Multisetup.help() else: if 'develop -u' in args: dirs.reverse() """ class Multisetup(object): """The main base class to build Multisetup instances In practice, you create a python script with this kind of code:: if __name__ == "__main__" from easydev.multisetup import Multisetup import sys packages = ['pkg1', 'pkg2'] mysetup = Multisetup(commands=sys.argv[1:], packages=packages) mysetup.run() """ def __init__(self, commands, packages=None, curdir='.', verbose=True): """.. rubric:: Constructor :param commands: list of user commands or command (see :meth:`parse_commands`) accepted commands are --packages, --exclude-packages, -quiet, --keep-going :param list packages: list of packages to process :param str curdir: current directory default is . :param bool verbose: verbose option :type commands: a string or list of strings The argument `commands` must be a list of strings combining arguments from multisetup and setup. :Examples: .. doctest:: :options: +SKIP >>> Multisetup("install --keep-going", ['pkg1', 'pkg2'], '.', verbose=True) >>> Multisetup(["install","--keep-going"], ['pkg1', 'pkg2'], '.', verbose=True) """ if len(commands) == 1 and commands[0] in ['-h', '--help']: Multisetup.help() if 'develop -u' in " ".join(commands): packages.reverse() # default self.curdir = os.path.abspath(curdir) if isinstance(commands, list): self.commands = list(commands) elif isinstance(commands, str): self.commands = list(commands.split(" ")) else: raise TypeError("commands argument must be a list of arguments or a string") self.packages = list(packages) self.verbose = verbose self.force = False # parsing user arguments self.parse_packages() # self.parse_intern_commands() self.parse_commands() @classmethod def help(cls): """help: to get more help and usage """ print(""" MultiSetup allows to build and install all the packages found in this directory usinf the same commands and setuptools. Examples: --------- # Developer mode : Installation of the pks from svn >>> python multisetup.py develop # User mode: Installation of the packages on the system as root >>> python multisetup.py install # Administrator mode: Create distribution of the packages >>> python multisetup.py nosetests -w test install bdist_egg -d ../dist sdist -d ../dist Common commands: multisetup.py sdist -d ./dist will create a source distribution underneath 'dist/' multisetup.py install will install the package Global options: --quiet do not show setup outputs [default=False] -k, --keep-going force the commands running[default=False] -h, --help show detailed help message --packages list of packages to run [default: none] --exclude-packages list of packages to not run print "usage: multisetup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] """) def parse_packages(self): """Search and remove package(S) from multisetup command (e.g., --package) .. todo:: known issue: python multisetup.py --packages with two packages will be confused by following commands. Must be put at the end of the command """ if '--packages' in self.commands: index = self.commands.index('--packages') self.commands.remove('--packages') self.packages = set() found = True while found is True: try: #test is no more argument self.commands[index] except: # then breaks break # otherwise if next argument starts with -, break if self.commands[index].startswith('-'): break # or carry on to gather package names else: self.packages.add(self.commands[index]) self.commands.remove(self.commands[index]) continue #self.commands.pop(index) if '--exclude-packages' in self.commands: # keep track of --exclude-package index index = self.commands.index('--exclude-packages') # remove it from the commands self.commands.remove('--exclude-packages') # remove all packages provided afterwards until next arguments is found found = True while found is True: # look for next argument/package that may be the end of the command try: package_to_remove = self.commands[index] except: break # if this is a valid package name if package_to_remove in self.packages: # remove it from the package list self.packages.remove(package_to_remove) # and from the command line self.commands.remove(package_to_remove) # until we found another package continue # otherwise, it is an argument that else: #starts with a - sign if package_to_remove.startswith('-'): break # or is invalid raise ValueError('--exclude-packages error: package %s not found in package list' \ % self.commands[index]) #self.commands.pop(index) def parse_commands(self): """Search and remove multisetup options Get the user command line arguments (self.commands) that are dedicated to multisetup such as --help, --quiet, --keep-going so that the remaining commands are fully comptatible with setuptools. """ if '--quiet' in self.commands: self.verbose = False self.commands.remove('--quiet') if '-k' in self.commands: self.force = True self.commands.remove('-k') if '--keep-going' in self.commands: self.force = True self.commands.remove('--keep-going') L = len(self.commands) i = 0 while (i < L): if self.commands[i].startswith('-'): try: self.commands[i-1] = self.commands[i-1] + ' ' + self.commands[i] + ' ' + self.commands[i+1] self.commands.pop(i) self.commands.pop(i) except: self.commands[i-1] = self.commands[i-1] + ' ' + self.commands[i] self.commands.pop(i) else: i += 1 L = len(self.commands) def run(self, color=True): """Executes 'python setup.py' with the user commands on all packages. """ if color: try: from easydev.console import bold, red, green, \ color_terminal, nocolor, underline, purple except: try: sys.path.insert(0, os.path.join('deploy', 'src', 'deploy')) from console import bold, red, green, \ color_terminal, nocolor, underline, purple except: pass if not color_terminal(): # Windows' poor cmd box doesn't understand ANSI sequences nocolor() else: bold = purple = red = green = underline = str print(bold("Running multisetup version %s" % __revision__.split()[2])) #project_dir = self.curdir.basename() directories = [package for package in self.packages] print('Will process the following directories: ',) for directory in directories: print(bold(directory)), #print bold(directory.basename()), print('') try: for directory in directories: try: os.chdir(directory) print(underline('Entering %s package' % os.path.basename(directory))) # % directory.basename()) except OSError as err: print(underline('Entering %s package' % os.path.basename(directory))) print(red("cannot find this directory (%s)" % os.path.basename(directory))) print(err) print('Python exec : ' , sys.executable) #print underline('Entering %s package' % directory.basename()) for cmd in self.commands: setup_command = '%s setup.py %s ' % (sys.executable,cmd) print("\tExecuting " + setup_command + '...processing',) #Run setup.py with user commands outputs = None errors = None if self.verbose: process = Popen(setup_command, shell=True) status = process.wait() else: process = Popen(setup_command, stdout=PIPE, stderr=PIPE, shell=True) #status = process.wait() outputs, errors = process.communicate() if process.returncode == 0: print(green('done')) else: if not self.verbose: print(red('\tFailed. ( error code %s) ' % (process.returncode))) os.chdir(self.curdir) if not self.force: raise RuntimeError() if 'pylint' in cmd: if outputs is not None: for x in outputs.split('\n'): if x.startswith('Your code has been'): print(purple('\t%s' % x)) if 'nosetests' in cmd: if errors is not None: for x in errors.split('\n'): if x.startswith('TOTAL'): res = x.replace('TOTAL', 'Total coverage') res = " ".join (res.split()) print(purple('\t%s' % res)) if x.startswith('Ran'): print(purple('\t%s' % x)) if x.startswith('FAILED'): print(purple('\t%s' % x)) else: print(purple('all right')) os.chdir(self.curdir) except RuntimeError: sys.exit() os.chdir(self.curdir) easydev-0.9.35/easydev/logging_tools.py0000664000175000017500000000566513052637441021547 0ustar cokelaercokelaer00000000000000# -*- python -*- # # This file is part of easydev software # # Copyright (c) 2012-2014 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # ############################################################################## import logging __all__ = ["Logging"] class Logging(object): """logging utility. When using the logging utility, it works like a singleton. So, once logging level is set, you cannot set it again easily. Here is a class that allows to do that. .. warning:: this is a bit of a hack. Maybe this is not a proper solution but it seems to do the job. :: >>> l = Logging("INFO") >>> l.info("test") INFO:root:test >>> l.level = "WARNING" >>> l.info("test") """ # I think that we can not inherit from logging. def __init__(self, level): """.. rubric:: constructor :param str level: valid levels are ["INFO", "DEBUG", "WARNING", "CRITICAL", "ERROR"]. If set to True, level is internally set to INFO. If set to False, level is seet internally to ERROR. """ self._debugLevel = None self.debugLevel = level self.logging = logging self.info = logging.info self.warning = logging.warning self.critical = logging.critical self.error = logging.error self.debug = logging.debug def _set_level(self, level): valid_level = [True, False, "INFO", "DEBUG", "WARNING", "CRITICAL", "ERROR"] if level is True: level = "INFO" if level is False: level = "ERROR" if level in valid_level: self._debugLevel = level else: raise ValueError("The level of debugging must be in %s " %valid_level) # I'm not sure this is the best solution, but basicConfig can be called # only once and populatse root.handlers list with one instance of # logging.StreamHandler. So, I reset it before calling basicConfig # that effectively changes the logging behaviour logging.root.handlers = [] logging.basicConfig(level=self._debugLevel) def _get_level(self): return self._debugLevel debugLevel = property(_get_level, _set_level, doc="Read/Write access to the debug level. Must be one of INFO, " + \ "DEBUG, WARNING, CRITICAL, ERROR") level = property(_get_level, _set_level, doc="alias to :attr:`~easydev.logging_tools.Logging.debugLevel` (Read-only access)") # Used copy/deepcopy module def __copy__(self): print("WARNING: easydev.logging_tools.__copy__ deprecated. use copy() instead") s = Logging(self.level) return s def __deepcopy__(self, memo): s = Logging(self.level) return s easydev-0.9.35/easydev/browser.py0000664000175000017500000001165513052631440020351 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## """Universal browser This module provides a browser in 2 flavours: as a program to use in a Terminal, or as a Python function that can be used in other software. The underlying code is based on the standard python module :mod:`webbrowser`. With webbrowser module itself, you can already open a URL as follows in a command line interface:: python -m webbrowser -t "http://www.python.org" However, with **browse**, you can simply type:: browse http://www.python.org It does not seem to be a big improvments but it is a bit more flexible. First, there is no need to enter "http://" : it will be added if missing and if this is not a local file.:: browse docs.python.org browse http://docs.python.org --verbose Similarly, you can open an image (it uses the default image viewer):: browse image.png Or a txt file (or any document provided there is a default executable to open it). It works like a charm under Linux. Under MAC, it uses the **open** command so this should also work. When invoking **browse**, under MacOSX, it actually tries to call **open** first and then calls webbrowser, if unsuccessful only. Note tested under Windows but uses webbrowser is used and works for open HTML document and URLs. You can also look at a directory (starts nautilus under Fedora):: browse ~/Pictures See more examples below. The interest of **browse** is that it can also be used programmatically:: from easydev.browser import browse # open an image with the default image viewer: browse("image.png") # or a web page browse("http://www.uniprot.org") There is also an alias **onweb**:: from easydev import onweb """ import os import sys, webbrowser from optparse import OptionParser import argparse def browse(url, verbose=True): from sys import platform as _platform if _platform == "linux" or _platform == "linux2": _browse_linux(url, verbose=True) elif _platform == "darwin": # under Mac, it looks like the standard webbrowser may not work as smoothly # OS X _browse_mac(url, verbose) elif _platform == "win32": # for windows and others, the same code as Linux should work _browse_linux(url, verbose=True) else: _browse_linux(url, verbose=True) def _browse_mac(url, verbose=True): if verbose: print("openning %s" % url) import os try: os.system("open /Applications/Safari.app {}".format(url)) return except: pass try: os.system("open /Applications/Safari.app {}".format("http://" + url)) return except: pass try: webbrowser.open_new(url) except: if verbose: print("Could not open %s. Trying to append http://" % url) try: webbrowser.open_new("open http://{}".format(url)) except: print("Could not open http://%s" % url) raise Exception def _browse_linux(url, verbose=True): if verbose: print("openning %s" % url) try: webbrowser.open(url) return except: pass try: if verbose: print("Could not open %s" % url) webbrowser.open("http://" + url) return except: pass raise Exception("Could not open http://{}".format(url)) def main(args=None): if args is None: args = sys.argv[:] # check for verbosity if "--verbose" in args: verbose = True args.remove("--verbose") print(args) else: verbose = False if "--help" in args or len(args) == 1: print("Browse, a simple command line browser") print("Author: Thomas Cokelaer, (c) 2012.") print("USAGE\n\tbrowse http://docs.python.org ") print("\tbrowse http://docs.python.org --verbose") print("\tbrowse localfile.html") print("\tbrowse local_directory (Linux only ?)") return url = args[1] if os.path.exists(url): if verbose: print("%s is local file. Trying to open it.\n" % url) browse(url, verbose) else: if verbose: print("%s seems to be a web address. Trying to open it.\n" %url) if url.startswith("http"): browse(url, verbose) else: if verbose: print("%s does not exists and does not starts with http, trying anyway." %url) browse("http://"+url, verbose) if __name__ == "__main__": import sys main(sys.argv) easydev-0.9.35/easydev/dependencies.py0000664000175000017500000000161613036730423021313 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## # $:Id $ import pkg_resources __all__ = ["get_dependencies"] def get_dependencies(pkgname): """Return dependencies of a package as a sorted list :param str pkgname: package name :return: list (empty list if no dependencies) """ try: res = pkg_resources.require(pkgname) res = list(set(res)) res.sort() return res except Exception: return [] easydev-0.9.35/easydev/timer.py0000664000175000017500000000204613036730577020015 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## """A convenient timer""" import time class Timer(): """Timer working with *with* statement :: times = [] with Timer(times): # do something import time time.sleep(0.1) with Timer(imes): # do something else time.sleep(0.2) """ def __init__(self, times): self.times = times def __enter__(self): self.t1 = time.time() def __exit__(self, type, value, traceback): self.t2 = time.time() self.times.append(self.t2-self.t1) easydev-0.9.35/easydev/copybutton.py0000664000175000017500000000656113036730465021105 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2014 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://packages.python.org/easydev # ############################################################################## """This module is a copy of a sphinx extension. unknown origin. added by Thomas Cokelaer: get_copybutton_path function. Create a sphinx extension based on copybutton javascript from python website Requires sphinx to be installed. imports are inside functions so not stricly speaking required for the installation. """ import os from os.path import join as pj import shutil try: from docutils import nodes except Exception: # if docutils is not installed class Dummy(): SkipNode = Exception nodes = Dummy() __all__ = ["get_copybutton_path", ] def copy_javascript_into_static_path(static="_static", filepath="copybutton.js"): """This script can be included in a sphinx configuration file to copy the copybutton in the static directory :param str static: name of the static path (_static by default) :param filename: full path of the file to copy :Details: If the path *static* does not exists, it is created. If the filename in filepath is already in the path *static*, nothing need to be done. Otherwise, the file is copied in *static* directory. """ if os.path.isdir(static): pass else: os.mkdir(static) filename = os.path.split(filepath)[1] if os.path.isfile(static + os.sep + filename): pass else: shutil.copy(filepath, static + os.sep + filename) def get_copybutton_path(): """Return the path where the to find the copybutton javascript Copy the copybutton.js javascript in the share directory of easydev so that it is accesible by all packages that required it by typing: .. doctest:: >>> from easydev import get_copybutton_path >>> p = get_copybutton_path() It can then be added with a Sphinx configuration file:: jscopybutton_path = easydev.copybutton.get_copybutton_path() """ import easydev try: # install mode packagedir = easydev.__path__[0] packagedir = os.path.realpath(pj(packagedir, 'share')) os.listdir(packagedir) # if this faisl, we are in deve mode except OSError: try: packagedir = easydev.__path__[0] packagedir = os.path.realpath(pj(packagedir, '..', 'share')) except: raise IOError("could not find data directory") return pj(packagedir, "copybutton.js") def setup(app): cwd = os.getcwd() # From Sphinx, we typing "make html", this is the place where we expect # the JS to be found staticpath = os.sep.join([cwd, "source", "_static"]) from easydev.tools import mkdirs mkdirs(staticpath) if os.path.exists(staticpath + os.sep + "copybutton.js"): pass # the JS file is already there. else: # Not found, so let us copy it import shutil shutil.copy( get_copybutton_path(), staticpath) # Now that the file is available, use it app.add_javascript('copybutton.js') easydev-0.9.35/easydev/url.py0000664000175000017500000000276413052635220017471 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## """Utilities related to the web""" try: import httplib except ImportError: import http.client as httplib __all__ = ["isurl_reachable"] def isurl_reachable(url, timeout=10, path="/"): """Checks if an URL exists or nor :param str url: the url to look for :param str path: Used in request.request at the url path following the domain name. For instance, www.ensembl.org is the site url but actually we want to check this full url www.ensembl.org/biomart/martview :return: True if it exists .. versionchanged:: 0.9.30 """ if url.startswith("http://") or url.startswith("https://"): url = url.split("//")[1] conn = httplib.HTTPConnection(url, timeout=timeout) try: conn.request("HEAD", path) except: return False # 302 is a redirection # 200 is okay try: response = conn.getresponse() except: return False if response.status in [200, 302]: return True else: return False easydev-0.9.35/easydev/package.py0000664000175000017500000003137513036730553020271 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## import os from easydev.logging_tools import Logging __all__ = ["PackageBuilder"] setup_template1 = """# -*- coding: utf-8 -*- __revision__ = "$Id$" # for the SVN Id import sys import os from setuptools import setup, find_packages import glob _MAJOR = %(MAJOR)s _MINOR = %(MINOR)s _MICRO = %(MICRO)s version = '%%d.%%d.%%d' %% (_MAJOR, _MINOR, _MICRO) release = '%%d.%%d' %% (_MAJOR, _MINOR) metainfo = { 'authors': {"main": ("%(author)s", "%(email)s")}, 'version': version, 'license' : 'GPL', 'download_url' : ['http://pypi.python.org/pypi/%(name)s'], 'url' : ["http://pythonhosted.org/%(name)s/"], 'description': "%(description)s" , 'platforms' : ['Linux', 'Unix', 'MacOsX', 'Windows'], 'keywords' : [''], 'classifiers' : [ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2.7', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Scientific/Engineering :: Bio-Informatics', 'Topic :: Scientific/Engineering :: Information Analysis', 'Topic :: Scientific/Engineering :: Mathematics', 'Topic :: Scientific/Engineering :: Physics'] } # files in share/data datadir = os.path.join('share','data') datafiles = [(datadir, [f for f in glob.glob(os.path.join(datadir, '*'))])] """ setup_template2 = """ setup( name = "%(name)s", version = version, maintainer = metainfo['authors']['main'][0], maintainer_email = metainfo['authors']['main'][1], author = metainfo['authors']['main'][0], author_email = metainfo['authors']['main'][1], long_description = open("README.txt").read(), keywords = metainfo['keywords'], description = metainfo['description'], license = metainfo['license'], platforms = metainfo['platforms'], url = metainfo['url'], download_url = metainfo['download_url'], classifiers = metainfo['classifiers'], # package installation package_dir = {'':'src'}, packages = ["%(pkgname)s"], install_requires = %(install_require)s, # uncomment if you have share/data files #data_files = datafiles, #use_2to3 = True, # causes issue with nosetests ) """ setup_template3 = """ namespace = '%(namespace)s' # messy but works for namespaces under Python 2.7 pkgs = [pkg for pkg in find_packages("src")] top_pkgs = [pkg for pkg in pkgs if len(pkg.split('.')) < 2] package_dir = {"": "src"} for pkg in top_pkgs: package_dir[namespace + "." + pkg] = "src" + os.sep + pkg setup( name = "%(name)s", version = version, maintainer = metainfo['authors']['main'][0], maintainer_email = metainfo['authors']['main'][1], author = metainfo['authors']['main'][0], author_email = metainfo['authors']['main'][1], long_description = open("README.txt").read(), keywords = metainfo['keywords'], description = metainfo['description'], license = metainfo['license'], platforms = metainfo['platforms'], url = metainfo['url'], download_url = metainfo['download_url'], classifiers = metainfo['classifiers'], # package installation package_dir = package_dir, packages = find_packages("src"), namespace_packages = [namespace], install_requires = %(install_require)s, # uncomment if you have share/data files #data_files = datafiles, #use_2to3 = True, # causes issue with nosetests ) """ namespace_init_template = """try: __import__('pkg_resources').declare_namespace(__name__) except: from pkgutil import extend_path __path__ = extend_path(__path__, __name__) """ namespace_init_pkg_template = """# Redirect path import os cdir = os.path.dirname(__file__) pdir = os.path.join(cdir, "../../%(pkgname)s") pdir = os.path.abspath(pdir) __path__ = [pdir] + __path__[:] from %(namespace)s.%(pkgname)s.__init__ import * del cdir del pdir """ setup_cfg_template = """ [build_sphinx] source_dir = doc/source build_dir = doc/build all_files = 1 [nosetests] where=test with_coverage= cover_package=%(name)s cover_erase= verbosity=2 ;cover_html= ;cover_html_dir=html logging_level=ERROR [upload_docs] upload_dir=doc/build/html/ """ class PackageBuilder(object): """simple class to automatically build a package layout .. doctest:: :options: +SKIP >>> from easydev import PackageBuilder >>> p = PackageBuilder(name="testpackage") >>> p.buildPackage() For the time being, this example creates the following layout:: pkg/ |-- doc | |-- source | |-- _static |-- README.txt |-- setup.py |-- share | |-- data |-- src | |-- pkg | |-- __init__.py |-- test You can avoid the shared directory creation. The namespace is not implemented so far. The doc directory is empty so far. The version is set to 0.0.1 Metainfo in the setup file need to be filed but the package should already be functional. New modules can be added inside the src/pkgname directory. """ def __init__(self, name, share=True, namespace=None): """.. rubric:: Constructor :param str name: :param str share: """ self.pkgname = name # can be only the pkg name without namespace self.name = name # can be pkg or namespace.pkg self.share = share self.namespace = namespace self.metainfo = { "pkgname": self.pkgname, "name":self.pkgname, "version": "version", "MINOR": '0', "MAJOR": '0', "MICRO": '1', "install_require": '[]', "description": "Put a short description here", "author": "yourname", "email": "email@whatever.org", "namespace": self.namespace, } if self.namespace: self.metainfo["name"] = ".".join([self.namespace, self.pkgname]) self.metainfo["url"] = os.sep.join([self.namespace, self.pkgname]) self.logging = Logging("INFO") #self.init() def init(self, force=False): self.logging.info("Creating the package directory") if os.path.isdir(self.pkgname): self.logging.warning("Directory %s already exists." % self.pkgname) self.logging.warning("You are about to delete this directory %s. " % self.pkgname) if force==True: res = "y" else: res = raw_input("Do you wish to proceed (y/n)?") if res == "y": import shutil shutil.rmtree(self.pkgname) os.mkdir(self.pkgname) return True else: return False else: os.mkdir(self.pkgname) return True def create_namespace(self): if self.namespace == None: self.logging.warning("namespace is not set, cannot create namespace directories") return dir1 = "src" + os.sep + self.namespace dir2 = "src" + os.sep + self.namespace + os.sep + self.pkgname self._mkdir(dir1) self._mkdir(dir2) f = open(self.pkgname + os.sep + dir1 + os.sep + "__init__.py", "w") f.write(namespace_init_template) f.close() f = open(self.pkgname + os.sep + dir2 + os.sep + "__init__.py", "w") f.write(namespace_init_pkg_template % self.metainfo) f.close() def create_setup(self): """Creates a setup.py file""" self.logging.info("Creating the setup.py file (you will need to update the metadata!") filename = self.pkgname + os.sep + "setup.py" f = open(filename, "w") f.write(setup_template1 % self.metainfo) if self.namespace == None: f.write(setup_template2 % self.metainfo) else: f.write(setup_template3 % self.metainfo) f.close() # config file filename = self.pkgname + os.sep + "setup.cfg" f = open(filename, "w") f.write("# a setup configuration") f.write(setup_cfg_template % self.metainfo) f.close() def create_readme(self): """Creates a README file""" self.logging.info("Creating a README file") filename = self.pkgname + os.sep + "README.txt" f = open(filename, "w") f.write("""Provide some information to users about this package.""") f.close() def create_sphinx_directory(self): """Create layout for sphinx documentation""" self._mkdir("doc") self._mkdir("doc" + os.sep + "source") self._mkdir("doc" + os.sep + "_static") def create_test_directory(self): """Create a test directory""" self._mkdir("test") def _mkdir(self, name): filename = self.pkgname + os.sep self.logging.info("Creating a directory %s " % (filename + name)) os.mkdir(filename + name) def create_src_directory(self): """Create a source directory and empty __init__ file""" self._mkdir("src") dirname = "src" + os.sep + self.pkgname self._mkdir(dirname) dirname = self.pkgname + os.sep + dirname self.logging.info("Create %s/__init__ file " % dirname) f = open(dirname + os.sep + "__init__.py", "w") f.write("""__version__ = "$Rev: 10 $" import pkg_resources try: version = pkg_resources.require("%(name)s")[0].version except: version = __version__ """ % self.metainfo) f.close() def create_share_directory(self): """Create share/data directory if required""" self._mkdir("share") self._mkdir("share" + os.sep + "data") def buildPackage(self, force=False): """Builds the entire package This function removes the directory "pkgname" if it exists, to create it back (empty), and then calsl the methods starting with "create" word. """ res = self.init(force=force) if res == False: return self.create_setup() self.create_readme() self.create_sphinx_directory() self.create_test_directory() self.create_src_directory() if self.share: self.create_share_directory() if self.namespace: self.create_namespace() import argparse class OptionsBuildPackage(argparse.ArgumentParser): """Options Parser for BuildPackage """ def __init__(self, args, version="1.0", prog="easydev_buildPackage"): usage = """USAGE: %s --pkgname pkgName""" % prog super(OptionsBuildPackage, self).__init__(usage=usage, prog=prog) self.add_general_options() self.options = self.parse_args(args) self.version = version def add_general_options(self): #self.add_argument('--version', # action='version', version=self.version) self.add_argument("--pkgname", dest="pkgname", help="Name of the package to be created") self.add_argument("--package", dest="pkgname", help="Name of the package to be created") self.add_argument("--namespace", dest="namespace", default=None, help="If provided, creates a namespace.") self.add_argument("--no-share-directory", action="store_false", help="if five, the share directory is not created") self.add_argument("--verbosity", dest="verbosity", default="INFO", help="set verbosity to INFO, WARNING or ERROR") def buildPackage(): """The executable easydev_buildPackage """ import sys if len(sys.argv) > 1: parser = OptionsBuildPackage(sys.argv[1:]) p = PackageBuilder( name=parser.options.pkgname, share=parser.options.no_share_directory, namespace=parser.options.namespace) p.logging.debugLevel = parser.options.verbosity p.buildPackage() else: parser = OptionsBuildPackage(["--help"]) easydev-0.9.35/easydev/options.py0000664000175000017500000000330013036730423020350 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## import argparse __all__ = ["SmartFormatter"] class SmartFormatter(argparse.HelpFormatter): """Formatter to be used with argparse ArgumentParser When using the argparse Python module one can design standalone applications with arguments (e.g; --help, --verbose...) One can easily define the help as well. However, The help message is wrapped by ArgumentParser, removing all formatting in the process. The reason being that the entire documentation is consistent. Sometines, one want to keep the format. This class can be used to do that. Example:: import argparse from easydev import SmartFormatter class Options(argparse.ArgumentParser): def __init__(self, prog="sequana"): usage = "blabla" description="blabla" super(Options, self).__init__(usage=usage, prog=prog, description=description, formatter_class=SmartFormatter) """ def _split_lines(self, text, width): if text.startswith('FORMAT|'): return text[7:].splitlines() # this is the RawTextHelpFormatter._split_lines return argparse.HelpFormatter._split_lines(self, text, width) easydev-0.9.35/easydev/codecs.py0000664000175000017500000000473113036714634020134 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://www.assembla.com/spaces/pyeasydev/wiki # Documentation: http://packages.python.org/easydev # ############################################################################## # $:Id $ """various type convertors (e.g., list to string)""" __all__ = ["to_list", "tolist", "list2string"] def tolist(data, verbose=True): """alias to :func:`easydev.codecs.to_list`""" return to_list(data, verbose=verbose) def to_list(data, verbose=True): """Transform an object into a list if possible :param data: a list, tuple, or simple type (e.g. int) :return: a list - if the object is list or tuple, do nothing - if the object is not a list, we assume this is a primitive type and a list of length 1 is returned, the item being the parameter provided. .. code-block:: python >>> from easydev import transform_to_list >>> to_list(1) [1] >>> to_list([1,2]) [1,2] """ if isinstance(data, list) or isinstance(data, tuple): return data #nothing to do elif isinstance(data, float): return [data] elif isinstance(data, int): return [data] elif isinstance(data, str): return [data] else: try: data = data.tolist() return data except: if verbose: print("not known type. Trying to cast into a list") return list(data) def list2string(data, sep=",", space=True): """Transform a list into a string :param list data: list of items that have a string representation. the input data could also be a simple object, in which case it is simply returned with a cast into a string :param str sep: the separator to be use .. code-block:: python >>> list2string([1, 2] "1, 2" >>> list2string([1, 2], sep=;) "1; 2" >>> list2string([1, 2], sep=;, space=False) "1;2" >>> list2string(1, sep=;, space=False) "1" .. note:: the cast is performed with str() """ data = to_list(data) if space is True: sep = sep + " " res = sep.join([str(x) for x in data]) return res easydev-0.9.35/easydev/config_tools.py0000664000175000017500000003337013052645074021361 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2014 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://packages.python.org/easydev # ############################################################################## try: from ConfigParser import ConfigParser except ImportError: from configparser import ConfigParser import os __all__ = ["CustomConfig", "DynamicConfigParser", "ConfigExample", "load_configfile"] # 53, 59, 64-65, 181, 260, 262, 267-270, 288-290, 329, 332-337, 340-341, 359-360, 386-388, 400-401, 406-426, 431-435 class _DictSection(object): """Dictionary section. Reference: https://gist.github.com/dangoakachan/3855920 """ def __init__(self, config, section): object.__setattr__(self, '_config', config) object.__setattr__(self, '_section', section) def __getattr__(self, attr): return self.get(attr, None) __getitem__ = __getattr__ def get(self, attr, default = None): if attr in self: return self._config.get(self._section, attr) else: return default def __setattr__(self, attr, value): if attr.startswith('_'): object.__setattr__(self, attr, value) else: self.__setitem__(attr, value) def __setitem__(self, attr, value): if self._section not in self._config: self._config.add_section(self._section) self._config.set(self._section, attr, str(value)) def __delattr__(self, attr): if attr in self: self._config.remove_option(self._section, attr) __delitem__ = __delattr__ def __contains__(self, attr): config = self._config section = self._section return config.has_section(section) and config.has_option(section, attr) class ConfigExample(object): """Create a simple example of ConfigParser instance to play with :: >>> from easydev.pipeline.config import ConfigExample >>> c = ConfigExample().config # The ConfigParser instance >>> assert 'General' in c.sections() >>> assert 'GA' in c.sections() This example builds up a ConfigParser instance from scratch. This is equivalent to having the following input file:: [General] verbose = True tag = test [GA] popsize = 1 which can be read with ConfigParser as follows: .. code-block:: python >>> from ConfigParser import ConfigParser >>> config = ConfigParser() >>> config.read("file.ini") [] """ def __init__(self): self.config = ConfigParser() self.config.add_section('General') self.config.set('General', 'verbose', 'true') self.config.add_section('GA') self.config.set('GA', 'popsize', '50') class DynamicConfigParser(ConfigParser, object): """Enhanced version of Config Parser Provide some aliases to the original ConfigParser class and new methods such as :meth:`save` to save the config object in a file. .. code-block:: python >>> from easydev.config_tools import ConfigExample >>> standard_config_file = ConfigExample().config >>> c = DynamicConfigParser(standard_config_file) >>> >>> # then to get the sections, simply type as you would normally do with ConfigParser >>> c.sections() >>> # or for the options of a specific sections: >>> c.get_options('General') You can now also directly access to an option as follows:: >>> c.General.tag Then, you can add or remove sections (:meth:`remove_section`, :meth:`add_section`), or option from a section :meth:`remove_option`. You can save the instance into a file or print it:: >>> print(c) .. warning:: if you set options manually (e.G. self.GA.test =1 if GA is a section and test one of its options), then the save/write does not work at the moment even though if you typoe self.GA.test, it has the correct value Methods inherited from ConfigParser are available: :: # set value of an option in a section c.set(section, option, value=None) # but with this class, you can also use the attribute c.section.option = value # set value of an option in a section c.remove_option(section, option) c.remove_section(section) """ def __init__(self, config_or_filename=None, *args, **kargs): object.__setattr__(self, '_filename', config_or_filename) # why not a super usage here ? Maybe there were issues related # to old style class ? ConfigParser.__init__(self, *args, **kargs) if isinstance(self._filename, str) and os.path.isfile(self._filename): self.read(self._filename) elif isinstance(config_or_filename, ConfigParser): self._replace_config(config_or_filename) elif config_or_filename == None: pass else: raise TypeError("config_or_filename must be a valid filename or valid ConfigParser instance") def read(self, filename): """Load a new config from a filename (remove all previous sections)""" if os.path.isfile(filename)==False: raise IOError("filename {0} not found".format(filename)) config = ConfigParser() config.read(filename) self._replace_config(config) def _replace_config(self, config): """Remove all sections and add those from the input config file :param config: """ for section in self.sections(): self.remove_section(section) for section in config.sections(): self.add_section(section) for option in config.options(section): data = config.get(section, option) self.set(section, option, data) def get_options(self, section): """Alias to get all options of a section in a dictionary One would normally need to extra each option manually:: for option in config.options(section): config.get(section, option, raw=True)# then, populate a dictionary and finally take care of the types. .. warning:: types may be changed .For instance the string "True" is interpreted as a True boolean. .. seealso:: internally, this method uses :meth:`section2dict` """ return self.section2dict(section) def section2dict(self, section): """utility that extract options of a ConfigParser section into a dictionary :param ConfigParser config: a ConfigParser instance :param str section: the section to extract :returns: a dictionary where key/value contains all the options/values of the section required Let us build up a standard config file: .. code-block:: python >>> import ConfigParser >>> c = ConfigParser.ConfigParser() >>> c.add_section('general') >>> c.set('general', 'step', str(1)) >>> c.set('general', 'verbose', 'True') To access to the step options, you would write:: >>> c.get('general', 'step') this function returns a dictionary that may be manipulated as follows:: >>> d_dict.general.step .. note:: a value (string) found to be True, Yes, true, yes is transformed to True .. note:: a value (string) found to be False, No, no, false is transformed to False .. note:: a value (string) found to be None; none, "" (empty string) is set to None .. note:: an integer is cast into an int """ options = {} for option in self.options(section): data = self.get(section, option, raw=True) if data.lower() in ['true', 'yes']: options[option] = True elif data.lower() in ['false', 'no']: options[option] = False elif data in ['None', None, 'none', '']: options[option] = None else: try: # numbers try: options[option] = self.getint(section, option) except: options[option] = self.getfloat(section, option) except: #string options[option] = self.get(section, option, raw=True) return options def save(self, filename): """Save all sections/options to a file. :param str filename: a valid filename :: config = ConfigParams('config.ini') #doctest: +SKIP config.save('config2.ini') #doctest: +SKIP """ try: if os.path.exists(filename) == True: print("Warning: over-writing %s " % filename) fp = open(filename,'w') except Exception as err: print(err) raise Exception('filename could not be opened') self.write(fp) fp.close() def add_option(self, section, option, value=None): """add an option to an existing section (with a value) .. code-block:: python >>> c = DynamicConfigParser() >>> c.add_section("general") >>> c.add_option("general", "verbose", True) """ assert section in self.sections(), "unknown section" #TODO I had to cast to str with DictSection self.set(section, option, value=str(value)) def __str__(self): str_ = "" for section in self.sections(): str_ += '[' + section + ']\n' for option in self.options(section): data = self.get(section, option, raw=True) str_ += option + ' = ' + str(data)+'\n' str_ += '\n\n' return str_ def __getattr__(self, key): return _DictSection(self, key) __getitem__ = __getattr__ def __setattr__(self, attr, value): if attr.startswith('_') or attr: object.__setattr__(self, attr, value) else: self.__setitem__(attr, value) def __setitem__(self, attr, value): if isinstance(value, dict): section = self[attr] for k, v in value.items(): section[k] = v else: raise TypeError('value must be a valid dictionary') def __delattr__(self, attr): if attr in self: self.remove_section(attr) def __contains__(self, attr): return self.has_section(attr) def __eq__(self, data): # FIXME if you read file, the string "True" is a string # but you may want it to be converted to a True boolean value if sorted(data.sections()) != sorted(self.sections()): print("Sections differ") return False for section in self.sections(): for option in self.options(section): try: if str(self.get(section, option,raw=True)) != \ str(data.get(section,option, raw=True)): print("option %s in section %s differ" % (option, section)) return False except: return False return True class CustomConfig(object): """Base class to manipulate a config directory""" def __init__(self, name, verbose=False): self.verbose = verbose from easydev import appdirs self.appdirs = appdirs.AppDirs(name) def init(self): sdir = self.appdirs.user_config_dir self._get_and_create(sdir) def _get_config_dir(self): sdir = self.appdirs.user_config_dir return self._get_and_create(sdir) user_config_dir = property(_get_config_dir, doc="return directory of this configuration file") def _get_and_create(self, sdir): if not os.path.exists(sdir): print("Creating directory %s " % sdir) try: self._mkdirs(sdir) except Exception: print("Could not create the path %s " % sdir) return None return sdir def _mkdirs(self, newdir, mode=0o777): """See :func:`easydev.tools.mkdirs`""" from easydev.tools import mkdirs mkdirs(newdir, mode) def remove(self): try: sdir = self.appdirs.user_config_dir os.rmdir(sdir) except Exception as err: raise Exception(err) def _load_configfile(configpath): "Tries to load a JSON or YAML file into a dict." try: with open(configpath) as f: try: import json return json.load(f) except ValueError: f.seek(0) # try again try: import yaml except ImportError: raise IOError("Config file is not valid JSON and PyYAML " "has not been installed. Please install " "PyYAML to use YAML config files.") try: return yaml.load(f) except yaml.YAMLError: raise IOError("Config file is not valid JSON or YAML. " "In case of YAML, make sure to not mix " "whitespace and tab indentation.") except Exception as err: raise(err) def load_configfile(configpath): "Loads a JSON or YAML configfile as a dict." config = _load_configfile(configpath) if not isinstance(config, dict): raise IOError("Config file must be given as JSON or YAML " "with keys at top level.") return config easydev-0.9.35/easydev/chunks.py0000664000175000017500000000254313036730430020156 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## #http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python # # Here's a generator that yields the chunks you want: # # def chunks(l, n): # """Yield successive n-sized chunks from l.""" # for i in range(0, len(l), n): # yield l[i:i+n] # #The issue here is that the chunks are not evenly sized chunks # __all__ = ['split_into_chunks'] try: range = xrange # py2 except: pass #py3 def split_into_chunks(items, maxchunks=10): """Split a list evenly into N chunks .. doctest:: >>> from easydev import split_into_chunks >>> data = [1,1,2,2,3,3] >>> list(split_into_chunks(data, 3)) [[1, 2], [1, 3], [2, 3]] """ chunks = [[] for _ in range(maxchunks)] for i, item in enumerate(items): chunks[i % maxchunks].append(item) return filter(None, chunks) easydev-0.9.35/easydev/__init__.py0000664000175000017500000000340713052645165020432 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2014 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://packages.python.org/easydev # ############################################################################## from __future__ import print_function #from __future__ import absolute_import __version__ = "0.9.34" import pkg_resources try: version = pkg_resources.require("easydev")[0].version except: version = __version__ from . import browser from .browser import browse as onweb from . import codecs from .codecs import * from . import chunks from .chunks import * from . import copybutton from .copybutton import * from . import decorators from .decorators import * from . import doc from .doc import * from . import easytest from .easytest import * from . import logging_tools from .logging_tools import * from . import sphinx_themes from .sphinx_themes import * from . import tools from .tools import * from . import multisetup from .md5tools import md5 from . import options from .options import * from . import paths from .paths import * from . import misc from .misc import * from . import package from .package import * from . import config_tools from .config_tools import * #from . import timer from .timer import Timer from . import url from .url import * #import dependencies from .dependencies import get_dependencies from . import multicore from .multicore import * from .progressbar import TextProgressBar, progress_bar, Progress from .profiler import do_profile easydev-0.9.35/easydev/easytest.py0000664000175000017500000000673713052636640020543 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2014 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://packages.python.org/easydev # ############################################################################## # $:Id $ # keep nose inside the functions to avoid dependencies import tempfile __all__ = ["assert_list_almost_equal", "trysetattr", "TempFile"] #from easydev.decorators import ifpandas def assert_list_almost_equal(first, second, places=None, deltas=None): """Combined version nose.tools.assert_almost_equal and assert_list_equal This function checks that 2 lists contain identical items. The equality between pair of items is checked with assert_almost_equal function, which means you can check for the places argument .. note:: there may be already some tools to check that either in nosetests or unittest but could not find. .. doctest:: >>> from easydev.easytest import assert_list_almost_equal >>> assert_list_almost_equal([0,0,1], [0,0,0.9999], places=3) >>> assert_list_almost_equal([0,0,1], [0,0,0.9999], deltas=1e-4) """ from nose.tools import assert_almost_equal for x, y in zip(first, second): # PYTHON 2.6 hack. This assert_almost_equal function # fails but I don't think this is correct. So let us # catch the TypeError try: assert_almost_equal(x, y, places=places, delta=deltas) except TypeError: pass def trysetattr(this, attrname, value, possible): """A common test pattern: try to set a non-writable attribute :: class A(object): def __init__(self): self._a = 1 self._b = 2 def _get_a(self): return self._a def _set_a(self, value): self._a = value a = property(_get_a, _get_b) def _get_b(self): return self._b b = property(_get_b) >>> o = A() >>> trysetattr(A, "a", 1, possible=True) >>> trysetattr(A, "b", 1, False) AssertionError """ if possible == True: a1 = True a2 = False else: a1 = False a2 = True try: setattr(this, attrname, value) assert a1 # if the setattr is possible, this should be True except Exception: assert a2 class TempFile(object): """A small wrapper around tempfile.NamedTemporaryFile function :: f = TempFile(suffix="csv") f.name f.delete() # alias to delete=False and close() calls """ def __init__(self, suffix='', dir=None): self.temp = tempfile.NamedTemporaryFile(suffix=suffix, delete=False, dir=dir) def delete(self): try: self.temp._closer.delete = True except: self.temp.delete = True self.temp.close() def _get_name(self): return self.temp.name name = property(_get_name) def __exit__(self, type, value, traceback): try: self.delete() except AttributeError: pass finally: self.delete() def __enter__(self): return self easydev-0.9.35/easydev/console.py0000664000175000017500000000632713052635726020343 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # It is a modified version of console.py from the sphinx software # # Copyright (c) 2011-2014 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://packages.python.org/easydev # ############################################################################## """Format colored consoled output. Modified from sphinx.util.console""" import os import sys __all__ = ["color_terminal", "get_terminal_width", "term_width_line"] from easydev.platform import is_windows if is_windows() is True: import colorama colorama.init() # colors and other functions from the attributes codes are added dynamically to # this __all__ variable codes = {} def get_terminal_width(): """Returns the current terminal width""" try: import termios, fcntl, struct call = fcntl.ioctl(0, termios.TIOCGWINSZ, struct.pack('hhhh', 0, 0, 0, 0)) _, width = struct.unpack('hhhh', call)[:2] terminal_width = width except (SystemExit, KeyboardInterrupt): raise except: # FALLBACK terminal_width = int(os.environ.get('COLUMNS', 80)) - 1 return terminal_width def term_width_line(text): """prints pruned version of the input text (limited to terminal width) :param str text: :return str text: """ _tw = get_terminal_width() if not codes: # if no coloring, don't output fancy backspaces return text + '\n' else: return text.ljust(_tw) + '\r' def color_terminal(): """Does terminal allows coloring :return: boolean""" if not hasattr(sys.stdout, 'isatty'): return False if not sys.stdout.isatty(): return False if 'COLORTERM' in os.environ: return True term = os.environ.get('TERM', 'dumb').lower() if term in ('xterm', 'linux') or 'color' in term: return True return False def __nocolor(): """set color codes off""" codes.clear() def __coloron(): """Set color codes on""" codes.update(_orig_codes) def _colorize(name, text): return codes.get(name, '') + text + codes.get('reset', '') def _create_color_func(name): def inner(text): return _colorize(name, text) globals()[name] = inner _attrs = { 'reset': '39;49;00m', 'bold': '01m', 'faint': '02m', 'standout': '03m', 'underline': '04m', 'blink': '05m', } for _name, _value in _attrs.items(): codes[_name] = '\x1b[' + _value _colors = [ ('black', 'darkgray'), ('darkred', 'red'), ('darkgreen', 'green'), ('brown', 'yellow'), ('darkblue', 'blue'), ('purple', 'fuchsia'), ('turquoise', 'teal'), ('lightgray', 'white'), ] for i, (dark, light) in enumerate(_colors): codes[dark] = '\x1b[%im' % (i+30) codes[light] = '\x1b[%i;01m' % (i+30) _orig_codes = codes.copy() for _name in codes: _create_color_func(_name) # dynamically set the colors for x in codes.keys(): __all__.append(x) easydev-0.9.35/easydev/sphinx_themes.py0000664000175000017500000000277413036730573021557 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## """Common tools to ease access to a easydev sphinx themes.""" import os from os.path import join as pj __all__ = ["get_path_sphinx_themes", "get_sphinx_themes"] def get_path_sphinx_themes(): """Returns the path where the sphinx themes can be found .. doctest:: >>> from easydev import sphinx_themes >>> themes_path = sphinx_themes.get_path_sphinx_themes() """ import easydev sharedir = easydev.get_shared_directory_path("easydev") sharedir = os.path.join(sharedir, "themes") return sharedir def get_sphinx_themes(): """Returns the sphinx themes found in easydev .. doctest:: >>> from easydev import sphinx_themes >>> themes = sphinx_themes.get_sphinx_themes() >>> "standard" in themes True """ from easydev import get_shared_directory_path sharedir = get_shared_directory_path("easydev") sharedir = pj(sharedir, "themes") themes = [x for x in os.listdir(sharedir) if x.startswith(".") == False] return themes easydev-0.9.35/easydev/progressbar.py0000664000175000017500000001334613130514143021213 0ustar cokelaercokelaer00000000000000# -*- python -*- # -*- coding: utf-8 -*- # # This file is part of the easydev software # # Copyright (c) 2011-2017 # # File author(s): Thomas Cokelaer # # Distributed under the GPLv3 License. # See accompanying file LICENSE.txt or copy at # http://www.gnu.org/licenses/gpl-3.0.html # # Website: https://github.com/cokelaer/easydev # Documentation: http://easydev-python.readthedocs.io # ############################################################################## """A progress bar copied and adapted from pyMC code (dec 2014)""" from __future__ import print_function import sys import time import uuid try: from IPython.core.display import HTML, Javascript, display except ImportError: pass __all__ = ['progress_bar', 'TextProgressBar', 'Progress'] class ProgressBar(object): def __init__(self, iterations, interval=None): self.iterations = iterations # if no interval provided, set it to 1% if interval is None: if iterations <= 100: interval = 1 # everything % of the data else: interval = int(iterations/100) self.interval = interval self.start = time.time() self.last = 0 def _percentage(self, i): if self.iterations !=0: return 100 * i / float(self.iterations) else: # could be 100 ? return 100 def _get_elapsed(self): return time.time() - self.start elapsed = property(_get_elapsed) class TextProgressBar(ProgressBar): """Use :class:`Progress`""" def __init__(self, iterations, printer, width=40, interval=None): self.fill_char = '-' self.width = width self.printer = printer ProgressBar.__init__(self, iterations, interval=interval) def animate(self, i, dummy=None): # dummy=None is for back-compatibility if dummy is not None: print("second argument in easydev.progress_bar.animate is deprecated. Update your code") # +1 if i starts at 0 and finishes at N-1 if divmod(i, self.interval)[1] != 0 and i != self.iterations: pass else: self.printer(self.progbar(i)) def progbar(self, i): # +1 if i starts at 0 and finishes at N-1 bar = self.bar(self._percentage(i)) return "[%s] %i of %i complete in %.1f sec" % ( bar, (i), self.iterations, round(self.elapsed, 1)) def bar(self, percent): all_full = self.width - 2 num_hashes = int(percent / 100 * all_full) bar = self.fill_char * num_hashes + ' ' * (all_full - num_hashes) info = '%d%%' % percent loc = (len(bar) - len(info)) // 2 return replace_at(bar, info, loc, loc + len(info)) def replace_at(str, new, start, stop): return str[:start] + new + str[stop:] def consoleprint(s): if sys.platform.lower().startswith('win'): print(s, '\r', end='') else: print('\r', s, end='') sys.stdout.flush() def ipythonprint(s): print('\r', s, end='') sys.stdout.flush() class IPythonNotebookPB(ProgressBar): """Use :class:`Progress`""" def __init__(self, iterations, interval=None): self.divid = str(uuid.uuid4()) self.sec_id = str(uuid.uuid4()) pb = HTML( """