Add ansicolor, prettysize, makeurl and README.
authorRadek Brich <radek.brich@devl.cz>
Wed, 03 Apr 2013 20:37:05 +0200
changeset 1 ee31f1bf17c1
parent 0 ee24ce33ab55
child 2 d3ffa15f5886
Add ansicolor, prettysize, makeurl and README.
README
pycolib/ansicolor.py
pycolib/makeurl.py
pycolib/prettysize.py
pycolib/soap.py
soap.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README	Wed Apr 03 20:37:05 2013 +0200
@@ -0,0 +1,9 @@
+Pycolib
+========
+
+Pycolib is library of small, mostly one-file, modules with little or not (inter)dependencies.
+
+ * ansicolor - change color of text in terminal using ANSI escape sequences
+ * makeurl - adjust string for using in URL by replacing symbols and combined unicode chars
+ * prettysize - print file sizes in human readable form
+ * soap - minimal SOAP client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycolib/ansicolor.py	Wed Apr 03 20:37:05 2013 +0200
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+
+(BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, BOLD) = range(0,9)
+
+names = ['black','red','green','yellow','blue','magenta','cyan','white']
+
+disabled = False  # set True to disable all colors
+
+def color(enable, fg=None, bg=None):
+    '''
+
+    color(1) -- switch to bold
+    color(1,1) -- red foreground
+    color(1,3,4) -- red on blue
+    color(0) -- reset
+
+    '''
+    global disabled
+    if disabled:
+        return ''
+
+    if isinstance(fg, str):
+        fg = names.index(fg)
+    if isinstance(bg, str):
+        bg = names.index(bg)
+
+    if enable:
+        code = '1'
+        if fg is not None:
+            code = ''
+            if fg >= 8:
+                fg -= 8
+                code += '1;'
+            code += str(30 + fg)
+        if bg is not None:
+            code += ';'
+            if bg >= 8:
+                bg -= 8
+                code += '1;'
+            code += str(40 + bg)
+        return "\033[" + code + "m"
+    else:
+        return "\033[0m"
+
+def setcolor(enable, fg=None, bg=None):
+    print(color(enable, fg, bg), end='')
+
+
+if __name__ == '__main__':
+    for c in range(0,8):
+        print(
+            color(1,c), names[c].ljust(20),
+            color(1,8+c), names[c].ljust(20),
+            color(0), sep='')
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycolib/makeurl.py	Wed Apr 03 20:37:05 2013 +0200
@@ -0,0 +1,28 @@
+from unicodedata import normalize, combining
+
+
+def uncombine(text):
+    return ''.join([c for c in normalize('NFKD', text) if not combining(c)])
+
+
+def makeurl(title):
+    norm = uncombine(title)
+    url = norm.lower()
+    url = url.replace(' ', '-')
+    url = url.replace('.', '-')
+    url = url.replace(':', '-')
+    url = url.replace('\'', '')
+    url = url.replace('(', '')
+    url = url.replace(')', '')
+    url = url.replace('\"', '')
+    url = url.replace('?', '')
+    url = url.strip('-')
+    url = url.replace('--', '-')
+    url = url.replace('--', '-')
+    return url
+
+
+def fix_unicode_chars(text):
+    # replace latin2-unencodable chars with their escape codes
+    return unicode(text.encode('iso-8859-2', 'backslashreplace').replace('\\x','#'), 'iso-8859-2')
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycolib/prettysize.py	Wed Apr 03 20:37:05 2013 +0200
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+#
+# prettysize - print data sizes in format easily readable by humans
+#
+# Copyright (c) 2011  Radek Brich <radek.brich@devl.cz>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+iec_prefixes = ('Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi')
+si_prefixes = 'kMGTPEZY'
+big_prefixes = 'KMGTPEZY'
+
+
+def prettysize(num, base=1024, prefixes=si_prefixes, unit='B', sep=' '):
+    '''Convert data size in bytes to human readable aproximation.
+
+    Beware that default output is invalid by recent standardization.
+    Still, this form was in use for ages.
+
+    For standardized output, see prettysize_si and prettysize_iec.
+
+    '''
+    prefixes = list(prefixes)
+    prefix = ''
+    num = float(num)
+    while True:
+        if num < base:
+            # format number
+            out = '{:.2f}'.format(num)
+            # cut off trailing zeros
+            out = out.rstrip('0')
+            if out.endswith('.'):
+                out = out[:-1]
+            return out + sep + prefix + unit
+        num /= base
+        prefix = prefixes.pop(0)
+
+    raise OverflowError('Number is too large.')
+
+
+def prettysize_si(num, **kw):
+    return prettysize(num, base=1000, **kw)
+
+
+def prettysize_short(num):
+    return prettysize(num, unit='', sep='', prefixes=big_prefixes)
+
+
+def prettysize_iec(num):
+    return prettysize(num, prefixes=iec_prefixes)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pycolib/soap.py	Wed Apr 03 20:37:05 2013 +0200
@@ -0,0 +1,147 @@
+# minisoap - minimal SOAP client
+#
+# Copyright (c) 2011, 2013  Radek Brich <radek.brich@devl.cz>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+
+from http.client import HTTPConnection
+from urllib.parse import urlparse
+from lxml import etree
+from collections import OrderedDict
+
+debug = 0
+HTTPConnection.debuglevel = debug
+
+
+class SoapError(Exception):
+    pass
+
+
+class SoapResponse(OrderedDict):
+    def __init__(self, name):
+        OrderedDict.__init__(self)
+        self.name = name
+
+    def _printout(self, out, res, level):
+        indent = level * 2 * ' '
+        for k in res.keys():
+            if type(res[k]) is list:
+                for row in res[k]:
+                    out.extend([indent, k, '\n'])
+                    self._printout(out, row, level+1)
+            else:
+                out.extend([indent, k, ': ', str(res[k]), '\n'])
+
+    def __str__(self):
+        out = [self.name, '\n']
+        self._printout(out, self, 1)
+        return ''.join(out).rstrip()
+
+
+class SoapClient:
+    def __init__(self, url, namespace):
+        self.url = url
+        self.parsedurl = urlparse(url)
+        self.namespace = namespace
+        self.conn = HTTPConnection(self.parsedurl.netloc)
+
+    def _build_soap_message(self, name, args):
+        SOAP = 'http://schemas.xmlsoap.org/soap/envelope/'
+        root = etree.Element('{%s}Envelope' % SOAP, nsmap={'soap':SOAP})
+        body = etree.SubElement(root, '{%s}Body' % SOAP)
+        func = etree.SubElement(body, '{%s}%s' % (self.namespace, name), nsmap={None:self.namespace})
+        for key in args.keys():
+            param = etree.SubElement(func, key)
+            param.text = str(args[key])
+        if debug:
+            print(etree.tostring(root, pretty_print=True).decode('utf8'))
+        return root
+
+    def _break_params(self, func, d):
+        for param in func.iterchildren():
+            if len(param) == 0:
+                d[param.tag] = param.text
+            else:
+                if not param.tag in d:
+                    d[param.tag] = []
+                newd = OrderedDict()
+                self._break_params(param, newd)
+                d[param.tag].append(newd)
+
+    def _break_soap_message(self, root):
+        if not root.tag.endswith('}Envelope'):
+            raise SoapError('Reply has no Envelope.')
+
+        SOAP = root.tag.split('}')[0][1:]
+
+        body_list = list(root.iterchildren('{%s}Body' % SOAP))
+        if len(body_list) != 1:
+            raise SoapError('Bad SOAP format (%d Body elements)' % len(body_list))
+        body = body_list[0]
+
+        func_list =list(body.iterchildren())
+        if len(func_list) != 1:
+            raise SoapError('Bad SOAP format (Body has %d children, should have one)' % len(body_list))
+        func = func_list[0]
+
+        if debug:
+            print(etree.tostring(func, pretty_print=True).decode('utf8'))
+        res = SoapResponse(func.tag)
+        self._break_params(func, res)
+
+        return res
+
+    def _serialize_xml(self, root):
+        return etree.tostring(root,
+            pretty_print=True,
+            encoding='utf-8',
+            xml_declaration=True)
+
+    def _parse_xml(self, file):
+        tree = etree.parse(file)
+        return tree.getroot()
+
+    def _send_soap(self, body):
+        headers = {
+            'Content-type': "text/xml;charset='utf-8'",
+            'Soapaction': "''",
+            'Accept': 'text/xml'}
+        self.conn.request('POST', self.parsedurl.path, body, headers)
+        resp = self.conn.getresponse()
+        if resp.status != 200:
+            raise SoapError('Server replied %d %s' % (resp.status, resp.reason))
+        return resp
+
+    def __getattr__(self, name):
+        def fnc(**kwargs):
+            root = self._build_soap_message(name, kwargs)
+            body = self._serialize_xml(root)
+            resp = self._send_soap(body)
+            root = self._parse_xml(resp)
+            return self._break_soap_message(root)
+
+        return fnc
+
+
+if __name__ == '__main__':
+    service = SoapClient('http://10.0.0.1:34567/', namespace='http://devl.cz/')
+    res = service.my_test_request()
+    print(res)
+
--- a/soap.py	Wed Apr 03 19:59:11 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,147 +0,0 @@
-# minisoap - minimal SOAP client
-#
-# Copyright (c) 2011, 2013  Radek Brich <radek.brich@devl.cz>
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-
-
-from http.client import HTTPConnection
-from urllib.parse import urlparse
-from lxml import etree
-from collections import OrderedDict
-
-debug = 0
-HTTPConnection.debuglevel = debug
-
-
-class SoapError(Exception):
-    pass
-
-
-class SoapResponse(OrderedDict):
-    def __init__(self, name):
-        OrderedDict.__init__(self)
-        self.name = name
-
-    def _printout(self, out, res, level):
-        indent = level * 2 * ' '
-        for k in res.keys():
-            if type(res[k]) is list:
-                for row in res[k]:
-                    out.extend([indent, k, '\n'])
-                    self._printout(out, row, level+1)
-            else:
-                out.extend([indent, k, ': ', str(res[k]), '\n'])
-
-    def __str__(self):
-        out = [self.name, '\n']
-        self._printout(out, self, 1)
-        return ''.join(out).rstrip()
-
-
-class SoapClient:
-    def __init__(self, url, namespace):
-        self.url = url
-        self.parsedurl = urlparse(url)
-        self.namespace = namespace
-        self.conn = HTTPConnection(self.parsedurl.netloc)
-
-    def _build_soap_message(self, name, args):
-        SOAP = 'http://schemas.xmlsoap.org/soap/envelope/'
-        root = etree.Element('{%s}Envelope' % SOAP, nsmap={'soap':SOAP})
-        body = etree.SubElement(root, '{%s}Body' % SOAP)
-        func = etree.SubElement(body, '{%s}%s' % (self.namespace, name), nsmap={None:self.namespace})
-        for key in args.keys():
-            param = etree.SubElement(func, key)
-            param.text = str(args[key])
-        if debug:
-            print(etree.tostring(root, pretty_print=True).decode('utf8'))
-        return root
-
-    def _break_params(self, func, d):
-        for param in func.iterchildren():
-            if len(param) == 0:
-                d[param.tag] = param.text
-            else:
-                if not param.tag in d:
-                    d[param.tag] = []
-                newd = OrderedDict()
-                self._break_params(param, newd)
-                d[param.tag].append(newd)
-
-    def _break_soap_message(self, root):
-        if not root.tag.endswith('}Envelope'):
-            raise SoapError('Reply has no Envelope.')
-
-        SOAP = root.tag.split('}')[0][1:]
-
-        body_list = list(root.iterchildren('{%s}Body' % SOAP))
-        if len(body_list) != 1:
-            raise SoapError('Bad SOAP format (%d Body elements)' % len(body_list))
-        body = body_list[0]
-
-        func_list =list(body.iterchildren())
-        if len(func_list) != 1:
-            raise SoapError('Bad SOAP format (Body has %d children, should have one)' % len(body_list))
-        func = func_list[0]
-
-        if debug:
-            print(etree.tostring(func, pretty_print=True).decode('utf8'))
-        res = SoapResponse(func.tag)
-        self._break_params(func, res)
-
-        return res
-
-    def _serialize_xml(self, root):
-        return etree.tostring(root,
-            pretty_print=True,
-            encoding='utf-8',
-            xml_declaration=True)
-
-    def _parse_xml(self, file):
-        tree = etree.parse(file)
-        return tree.getroot()
-
-    def _send_soap(self, body):
-        headers = {
-            'Content-type': "text/xml;charset='utf-8'",
-            'Soapaction': "''",
-            'Accept': 'text/xml'}
-        self.conn.request('POST', self.parsedurl.path, body, headers)
-        resp = self.conn.getresponse()
-        if resp.status != 200:
-            raise SoapError('Server replied %d %s' % (resp.status, resp.reason))
-        return resp
-
-    def __getattr__(self, name):
-        def fnc(**kwargs):
-            root = self._build_soap_message(name, kwargs)
-            body = self._serialize_xml(root)
-            resp = self._send_soap(body)
-            root = self._parse_xml(resp)
-            return self._break_soap_message(root)
-
-        return fnc
-
-
-if __name__ == '__main__':
-    service = SoapClient('http://10.0.0.1:34567/', namespace='http://devl.cz/')
-    res = service.my_test_request()
-    print(res)
-