From 1d5335015056b398ad5d0736d0bcdf5d03efa290 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Fri, 14 Oct 2016 00:01:26 +0200 Subject: [PATCH 01/58] add test via pytest and tox, tools/conosle/pagan, minor changes --- README.md | 19 ++++++++++++++ pagan/generator.py | 43 +++++++++++++++++++++++++++---- pagan/pagan.py | 11 +++++--- setup.py | 1 + test/test_generator.py | 25 ++++++++++++++++++ test/test_pagan.py | 58 ++++++++++++++++++++++++++++++++++++++++++ tools/console/pagan | 38 +++++++++++++++++++++++++++ tox.ini | 13 ++++++++++ 8 files changed, 200 insertions(+), 8 deletions(-) create mode 100644 test/test_generator.py create mode 100644 test/test_pagan.py create mode 100644 tools/console/pagan create mode 100644 tox.ini diff --git a/README.md b/README.md index fde1e12..f16a183 100644 --- a/README.md +++ b/README.md @@ -98,3 +98,22 @@ sha224 | pagan.SHA224 sha256 | pagan.SHA256 sha384 | pagan.SHA384 sha512 | pagan.SHA512 + + +###Testing + +####Using py.test + +``` +>> pip install pytest +>> pytest +``` + +####Using tox + +``` +>> pip install tox +>> tox +``` + +you might have to adjust the python version in tox.ini to your needs. diff --git a/pagan/generator.py b/pagan/generator.py index b6b0ce7..3b3adc9 100644 --- a/pagan/generator.py +++ b/pagan/generator.py @@ -1,8 +1,17 @@ +# -*- coding: latin-1 -*- + from PIL import Image, ImageDraw -from . import hashgrinder -from . import pgnreader +import hashgrinder +import pgnreader import hashlib import os +import sys + + +class FalseHashError(Exception): + """ """ + pass + # Set True to generate debug output in this module. DEBUG = False @@ -10,7 +19,7 @@ # Default output path is the current working directory. OUTPUT_PATH = ('%s%soutput%s' % (os.getcwd(), os.sep, os.sep)) -#Directory of the installed module. +# Directory of the installed module. PACKAGE_DIR = os.path.dirname(os.path.abspath(__file__)) # Actual Image size in pixel is fixed in this version of pagan. @@ -94,7 +103,12 @@ def hash_input(inpt, algo=HASH_SHA256): elif (algo == HASH_SHA512): hashcode = hashlib.sha512() - hashcode.update(inpt.encode('utf-8')) + if sys.version_info.major == 2: + inpt = bytes(inpt) + else: + inpt = bytes(inpt, "utf-8") + + hashcode.update(inpt) hexhash = hashcode.hexdigest() return hexhash @@ -262,4 +276,23 @@ def generate(str, alg): hashcode = hash_input(str, alg) pixelmap = setup_pixelmap(hashcode) draw_image(pixelmap, img) - return img \ No newline at end of file + return img + + +def generate_by_hash(hashcode): + """Generates an PIL image avatar based on the given + hash String. Acts as the main accessor to pagan.""" + img = Image.new(IMAGE_MODE, IMAGE_SIZE, BACKGROUND_COLOR) + if len(hashcode) < 32: + print ("hashcode must have lenght >= 32, %s" % hashcode) + raise FalseHashError + + allowed = "0123456789abcdef" + hashcheck = [c in allowed for c in hashcode] + if False in hashcheck: + print ("hashcode has not allowed structure %s" % hashcode) + raise FalseHashError + + pixelmap = setup_pixelmap(hashcode) + draw_image(pixelmap, img) + return img diff --git a/pagan/pagan.py b/pagan/pagan.py index d7673e1..c561659 100644 --- a/pagan/pagan.py +++ b/pagan/pagan.py @@ -1,11 +1,13 @@ -from . import generator +# -*- coding: latin-1 -*- +import generator import os class Avatar(): # Default output path is in the current working directory. - DEFAULT_OUTPUT_PATH = ('%s/output/' % os.getcwd()) + DEFAULT_OUTPUT_PATH = os.path.join(os.getcwd(), "output/") + # Default filename. DEFAULT_FILENAME = ('pagan') @@ -19,7 +21,8 @@ def __create_image(self, inpt, hashfun): """Creates the avatar based on the input and the chosen hash function.""" if hashfun not in generator.HASHES.keys(): - print ("Unknown or unsupported hash function. Using default: %s" % self.DEFAULT_HASHFUN) + print ("Unknown or unsupported hash function. Using default: %s" + % self.DEFAULT_HASHFUN) algo = self.DEFAULT_HASHFUN else: algo = hashfun @@ -52,5 +55,7 @@ def save(self, path=DEFAULT_OUTPUT_PATH, filename=DEFAULT_FILENAME): # Saves the image under the given filepath. filepath = ("%s%s.png" % (path, filename)) + filepath = os.path.join(path, "%s.png" % filename) + # FIXIT: filepath without SUFFIX, print writes false filename print ("Saving: %s" % filepath) self.img.save(filepath, 'PNG') diff --git a/setup.py b/setup.py index 7fa16c9..1afbf97 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,7 @@ author='David Bothe', author_email='davbothe@googlemail.com', keywords=['avatar', 'identicon', 'generator'], + scripts=['tools/console/pagan'], classifiers=[ 'Programming Language :: Python', 'Programming Language :: Python :: 3', diff --git a/test/test_generator.py b/test/test_generator.py new file mode 100644 index 0000000..a147b3c --- /dev/null +++ b/test/test_generator.py @@ -0,0 +1,25 @@ +import pagan +import pytest +import sys + + +def test_generate_by_hash(): + import hashlib + md5 = hashlib.md5() + inpt = "" + if sys.version_info.major == 2: + inpt = bytes(inpt) + else: + inpt = bytes(inpt, "utf-8") + + md5.update(inpt) + img = pagan.generator.generate_by_hash(md5.hexdigest()) + assert img is not None + img2 = pagan.generator.generate("", 0) + assert img == img2 + + with pytest.raises(pagan.generator.FalseHashError): + img = pagan.generator.generate_by_hash(md5.hexdigest()[:2]) + + with pytest.raises(pagan.generator.FalseHashError): + img = pagan.generator.generate_by_hash(md5.hexdigest()+"A") diff --git a/test/test_pagan.py b/test/test_pagan.py new file mode 100644 index 0000000..ad41ce6 --- /dev/null +++ b/test/test_pagan.py @@ -0,0 +1,58 @@ +# -*- coding: latin-1 -*- +import tempfile +import pagan + + +def test_create(): + img0 = pagan.Avatar("") + img1 = pagan.Avatar("", 0) + img2 = pagan.Avatar("", -111) + img3 = pagan.Avatar("", None) + assert img2.img == img3.img + assert img0.img == img1.img + + +def test_diffrent_hash(): + img0 = pagan.Avatar("", 0) + img1 = pagan.Avatar("", 1) + assert img0.img != img1.img + + +def test_umlaute(): + img0 = pagan.Avatar("äöüÄÖÜß") + img1 = pagan.Avatar("äöüÄÖÜß", 4) + assert img0.img != img1.img + + +def test_show(): + """check only if error is raised""" + img0 = pagan.Avatar("You rock!") + img0.show() + + +def test_change(): + img0 = pagan.Avatar("1") + img1 = pagan.Avatar("0") + assert img0.img != img1.img + img0.change("0") + assert img0.img == img1.img + + +def test_save(): + img0 = pagan.Avatar("1") + tmpdir = tempfile.gettempdir() + tmpfile = tempfile.mkstemp(".png", dir=tmpdir)[1] + img0.save("/", tmpfile) + img0.save(tmpdir, tmpfile) + tmpfile = tempfile.mkstemp("", dir=tmpdir)[1] + img0.save("/", tmpfile) + img0.save(tmpdir, tmpfile) + + +if __name__ == "__main__": + test_create() + test_diffrent_hash() + test_umlaute() + test_show() + test_change() + test_save() diff --git a/tools/console/pagan b/tools/console/pagan new file mode 100644 index 0000000..30bc986 --- /dev/null +++ b/tools/console/pagan @@ -0,0 +1,38 @@ +#!/usr/bin/env python +"""call pagan from console +""" + +import argparse +import os +import pagan +from pagan import generator + +parser = argparse.ArgumentParser() +parser.add_argument("slogan", nargs="+", + help="string, basis of avatar computing") +parser.add_argument("--show", action="store_true", + help="show avatar in external editor, default behavior") +parser.add_argument("--output", + help="save image to output path") +parser.add_argument("--hash", default="MD5", + help="use hash function, allowed %s, default %s" % + (generator.HASHES.values(), generator.HASHES.values()[0])) + +args = parser.parse_args() + +slogan = " ".join(args.slogan) + +if args.hash and args.hash not in generator.HASHES.values(): + HASH = "" +elif args.hash and args.hash in generator.HASHES.values(): + HASH = generator.HASHES.values().index(args.hash) + +if not args.output: + args.show = True + +img = pagan.Avatar(slogan, HASH) +if args.show: + img.show() + +if args.output: + img.save(*os.path.split(args.output)) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..cd1c311 --- /dev/null +++ b/tox.ini @@ -0,0 +1,13 @@ +# Tox (http://tox.testrun.org/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = py27, py34, py35 +skip_missing_interpreters = True + +[testenv] +commands = pytest +deps = + pytest From d8789a4c568f88f6752e050b20ac6257702b850f Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Fri, 14 Oct 2016 00:14:33 +0200 Subject: [PATCH 02/58] add readme, log warning webserver --- tools/webserver/Readme.md | 2 + tools/webserver/webserver.py | 118 +++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 tools/webserver/Readme.md create mode 100644 tools/webserver/webserver.py diff --git a/tools/webserver/Readme.md b/tools/webserver/Readme.md new file mode 100644 index 0000000..501ca58 --- /dev/null +++ b/tools/webserver/Readme.md @@ -0,0 +1,2 @@ +requirement: + bottle diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py new file mode 100644 index 0000000..e6bde68 --- /dev/null +++ b/tools/webserver/webserver.py @@ -0,0 +1,118 @@ +"""pagan webserver""" +from bottle import debug +from bottle import error +from bottle import post +from bottle import request +from bottle import response +from bottle import route +from bottle import run +from bottle import static_file +from bottle import template +import hashlib +import logging +import pagan +import tempfile + +logging.warning("\n%s\n\ +early work in progress, try at your own risk\n\ +this will pollute your temp dir!\n\ +%s" % ("= "*32, "= "*32)) + +TEMPLATEINDEX = """ +
+

pagan

+
+Welcome to the python avatar generator for absolute nerds. +

Generate your own Avatar. +

+

+
+
+
Current SearchPrevious Search
+

{{slogan}} +

+

+ +

{{hist1}} +

+

{{hist2}} +

+

{{hist3}} +

+

+""" + + +@error(404) +def error404(code): + """handle error 404 """ + return template('{{code}} Avatar not found. You may use this:', + code=code) + + +@route('/') +@post('/') +def index(): + """main functionality of webserver""" + default = ["pagan", "python", "avatar", "github"] + slogan = request.forms.get("slogan") + + if not slogan: + if request.get_cookie("hist1"): + slogan = request.get_cookie("hist1") + else: + slogan = "pagan" + + if not request.get_cookie("hist1"): + hist1, hist2, hist3, hist4 = default[:] + else: + hist1 = request.get_cookie("hist1") + hist2 = request.get_cookie("hist2") + hist3 = request.get_cookie("hist3") + hist4 = request.get_cookie("hist4") + + if slogan in (hist1, hist2, hist3, hist4): + history = [hist1, hist2, hist3, hist4] + history.remove(slogan) + hist1, hist2, hist3 = history[0], history[1], history[2] + + response.set_cookie("hist1", slogan, max_age=60*60*24*30, httponly=True) + response.set_cookie("hist2", hist1, max_age=60*60*24*30, httponly=True) + response.set_cookie("hist3", hist2, max_age=60*60*24*30, httponly=True) + response.set_cookie("hist4", hist3, max_age=60*60*24*30, httponly=True) + # slogan, hist1, hist2, hist3 = escape(slogan), escape(hist1),\ + # escape(hist2), escape(hist3) + md5 = hashlib.md5() + md5.update(slogan) + slogan_hash = md5.hexdigest() + md5.update(hist1) + hist1_hash = md5.hexdigest() + md5.update(hist2) + hist2_hash = md5.hexdigest() + md5.update(hist3) + hist3_hash = md5.hexdigest() + return template(TEMPLATEINDEX, slogan=slogan, + hist1=hist1, hist2=hist2, hist3=hist3, + sloganHash=slogan_hash, hist1Hash=hist1_hash, + hist2Hash=hist2_hash, hist3Hash=hist3_hash) + + +@route('/himage/') +def hashimage(hashvalue): + """generate image by hash, usese tempfile :-/""" + tmpf = tempfile.mkstemp(".png")[1] + image = pagan.Avatar("") + image.img = pagan.generator.generate_by_hash(hashvalue) + image.save("/", tmpf) + return static_file(tmpf, root="/") + +debug(True) +run(host='localhost', port=8080) From 7e68ac12b7daaf43ab89dcf1e64bbe58b896aa29 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Fri, 14 Oct 2016 01:56:38 +0200 Subject: [PATCH 03/58] check for raise FalseHashError on buggy hash values as input for images --- tools/webserver/webserver.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index e6bde68..a4e59fe 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -110,7 +110,10 @@ def hashimage(hashvalue): """generate image by hash, usese tempfile :-/""" tmpf = tempfile.mkstemp(".png")[1] image = pagan.Avatar("") - image.img = pagan.generator.generate_by_hash(hashvalue) + try: + image.img = pagan.generator.generate_by_hash(hashvalue) + except pagan.generator.FalseHashError: + return "HashError, see log and check hash value, %s" % hashvalue image.save("/", tmpf) return static_file(tmpf, root="/") From cb2b56287e0a6e9da7894e5f1937fb344f5e542e Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Fri, 14 Oct 2016 01:56:38 +0200 Subject: [PATCH 04/58] check for raise FalseHashError on buggy hash values as input for images --- tools/webserver/webserver.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index e6bde68..a4e59fe 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -110,7 +110,10 @@ def hashimage(hashvalue): """generate image by hash, usese tempfile :-/""" tmpf = tempfile.mkstemp(".png")[1] image = pagan.Avatar("") - image.img = pagan.generator.generate_by_hash(hashvalue) + try: + image.img = pagan.generator.generate_by_hash(hashvalue) + except pagan.generator.FalseHashError: + return "HashError, see log and check hash value, %s" % hashvalue image.save("/", tmpf) return static_file(tmpf, root="/") From c0eb637935f3437dc394711c5fc6d8909ccc8b80 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Mon, 17 Oct 2016 11:53:50 +0200 Subject: [PATCH 05/58] allowed hash can contain upper and lower chars --- pagan/generator.py | 22 +++++++++++++--------- test/test_generator.py | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/pagan/generator.py b/pagan/generator.py index 3b3adc9..690c4f3 100644 --- a/pagan/generator.py +++ b/pagan/generator.py @@ -270,8 +270,10 @@ def setup_pixelmap(hashcode): def generate(str, alg): - """Generates an PIL image avatar based on the given - input String. Acts as the main accessor to pagan.""" + """Generates an PIL image avatar based on the given input String. + + Acts as the main accessor to pagan. + """ img = Image.new(IMAGE_MODE, IMAGE_SIZE, BACKGROUND_COLOR) hashcode = hash_input(str, alg) pixelmap = setup_pixelmap(hashcode) @@ -280,18 +282,20 @@ def generate(str, alg): def generate_by_hash(hashcode): - """Generates an PIL image avatar based on the given - hash String. Acts as the main accessor to pagan.""" + """Generates an PIL image avatar based on the given hash String. + + Acts as the main accessor to pagan. + """ img = Image.new(IMAGE_MODE, IMAGE_SIZE, BACKGROUND_COLOR) if len(hashcode) < 32: print ("hashcode must have lenght >= 32, %s" % hashcode) raise FalseHashError - allowed = "0123456789abcdef" - hashcheck = [c in allowed for c in hashcode] - if False in hashcheck: - print ("hashcode has not allowed structure %s" % hashcode) - raise FalseHashError + allowed = "0123456789ABCDEFabcdef" + for c in hashcode: + if c not in allowed: + print ("hashcode has not allowed structure %s" % hashcode) + raise FalseHashError pixelmap = setup_pixelmap(hashcode) draw_image(pixelmap, img) diff --git a/test/test_generator.py b/test/test_generator.py index a147b3c..b9a488e 100644 --- a/test/test_generator.py +++ b/test/test_generator.py @@ -22,4 +22,4 @@ def test_generate_by_hash(): img = pagan.generator.generate_by_hash(md5.hexdigest()[:2]) with pytest.raises(pagan.generator.FalseHashError): - img = pagan.generator.generate_by_hash(md5.hexdigest()+"A") + img = pagan.generator.generate_by_hash(md5.hexdigest()+"G") From 34fb9741ff939decc63eb9a70faf83500fc6d9f9 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Mon, 17 Oct 2016 11:59:26 +0200 Subject: [PATCH 06/58] fix merge relicts add test: save in new dir removed print statements changed 404 page more changes on 404 page of webserver --- test/test_generator.py | 4 ---- test/test_pagan.py | 15 +++++++++++++++ tools/webserver/webserver.py | 11 ++++++++--- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/test/test_generator.py b/test/test_generator.py index 7cd9a8d..b9a488e 100644 --- a/test/test_generator.py +++ b/test/test_generator.py @@ -22,8 +22,4 @@ def test_generate_by_hash(): img = pagan.generator.generate_by_hash(md5.hexdigest()[:2]) with pytest.raises(pagan.generator.FalseHashError): -<<<<<<< HEAD img = pagan.generator.generate_by_hash(md5.hexdigest()+"G") -======= - img = pagan.generator.generate_by_hash(md5.hexdigest()+"A") ->>>>>>> webserver diff --git a/test/test_pagan.py b/test/test_pagan.py index ad41ce6..f2ac4b5 100644 --- a/test/test_pagan.py +++ b/test/test_pagan.py @@ -1,4 +1,7 @@ # -*- coding: latin-1 -*- +import os +import random +import string import tempfile import pagan @@ -49,6 +52,17 @@ def test_save(): img0.save(tmpdir, tmpfile) +def test_save_new_dir(): + """save file in not existing directory""" + img0 = pagan.Avatar("1") + tmpdir = tempfile.gettempdir() + testdir = ''.join(random.SystemRandom().choice(string.digits) + for _ in range(12)) + testdir = os.path.join(tmpdir, testdir) + testfile = tempfile.mkstemp("", dir=tmpdir)[1].split("/")[-1] + img0.save(testdir, testfile) + + if __name__ == "__main__": test_create() test_diffrent_hash() @@ -56,3 +70,4 @@ def test_save(): test_show() test_change() test_save() + test_save_new_dir() diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index a4e59fe..664edf4 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -54,8 +54,12 @@ @error(404) def error404(code): """handle error 404 """ - return template('{{code}} Avatar not found. You may use this:', - code=code) + return """
+ + + I am the guard of this server and I am sorry to tell you:\ +

404 Avatar/Page not found.

Please go back to the \ + front door.""" @route('/') @@ -113,7 +117,8 @@ def hashimage(hashvalue): try: image.img = pagan.generator.generate_by_hash(hashvalue) except pagan.generator.FalseHashError: - return "HashError, see log and check hash value, %s" % hashvalue + return template("HashError, see log and check hash value, \ + {{hashvalue}}", hashvalue=hashvalue) image.save("/", tmpf) return static_file(tmpf, root="/") From 047a895f4e11a690f56e2410e299b2532bf1ca1e Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 18 Oct 2016 21:41:28 +0200 Subject: [PATCH 07/58] use bottle app, changed 404 message --- tools/webserver/webserver.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index a4e59fe..205e84c 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -1,10 +1,8 @@ """pagan webserver""" +from bottle import Bottle from bottle import debug -from bottle import error -from bottle import post from bottle import request from bottle import response -from bottle import route from bottle import run from bottle import static_file from bottle import template @@ -50,16 +48,19 @@

""" +app = Bottle() -@error(404) + +@app.error(404) def error404(code): """handle error 404 """ - return template('{{code}} Avatar not found. You may use this:', - code=code) + return """

These aren't
the pages you're
looking for.

the pagan + warriors - to protect and to serve""" -@route('/') -@post('/') +@app.route('/') +@app.post('/') def index(): """main functionality of webserver""" default = ["pagan", "python", "avatar", "github"] @@ -105,7 +106,7 @@ def index(): hist2Hash=hist2_hash, hist3Hash=hist3_hash) -@route('/himage/') +@app.route('/himage/') def hashimage(hashvalue): """generate image by hash, usese tempfile :-/""" tmpf = tempfile.mkstemp(".png")[1] @@ -117,5 +118,15 @@ def hashimage(hashvalue): image.save("/", tmpf) return static_file(tmpf, root="/") + +@app.route('/coverage_exit') +def coverage_exit(): + """exit function for coverage""" + import os + import signal + if os.environ["COVERAGE_EXIT"] != "True": + return "" + os.kill(os.getpid(), signal.SIGTERM) + debug(True) -run(host='localhost', port=8080) +run(app, host='localhost', port=8080) From 00e0fe7de9c7d5d6eeda48073b261ec9a4ef7208 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 18 Oct 2016 21:52:14 +0200 Subject: [PATCH 08/58] better 404, todo ideas --- tools/webserver/webserver.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index 205e84c..6794332 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -11,6 +11,10 @@ import pagan import tempfile +# TODO() Long input on main page destroys page +# TODO() Better Layout on main page +# TODO() Beautify 404 Error + logging.warning("\n%s\n\ early work in progress, try at your own risk\n\ this will pollute your temp dir!\n\ @@ -54,9 +58,13 @@ @app.error(404) def error404(code): """handle error 404 """ - return """

These aren't
the pages you're
looking for.

the pagan - warriors - to protect and to serve""" + return """ +
+ +

the pagan warriors - to protect and to serve +
+

These aren't
the pages you're
looking for.

+
""" @app.route('/') From 3d9daa4123d24c781d2b21609428d4c8c1a07067 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 18 Oct 2016 21:58:16 +0200 Subject: [PATCH 09/58] branch issue, taken stuff from master to webserver branch --- tools/webserver/webserver.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index 6794332..1ac32f3 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -59,12 +59,11 @@ def error404(code): """handle error 404 """ return """ -
- -

the pagan warriors - to protect and to serve -
-

These aren't
the pages you're
looking for.

-
""" + + + I am the guard of this server and I am sorry to tell you:\ +

404 Avatar/Page not found.

Please go back to the \ + front door.""" @app.route('/') @@ -122,7 +121,8 @@ def hashimage(hashvalue): try: image.img = pagan.generator.generate_by_hash(hashvalue) except pagan.generator.FalseHashError: - return "HashError, see log and check hash value, %s" % hashvalue + return template("HashError, see log and check hash value, \ + {{hashvalue}}", hashvalue=hashvalue) image.save("/", tmpf) return static_file(tmpf, root="/") From 968e61ae4cb7cd5d36d8a17f294721ee81dfd7f1 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 00:32:29 +0200 Subject: [PATCH 10/58] minor changes --- tools/webserver/webserver.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index 1ac32f3..ce2c09c 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -136,5 +136,6 @@ def coverage_exit(): return "" os.kill(os.getpid(), signal.SIGTERM) -debug(True) -run(app, host='localhost', port=8080) +if __name__ == "__main__": + debug(True) + run(app, host='localhost', port=8080) From 7fb59968351c8aca05424f57c979f15faa6f289c Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 01:25:25 +0200 Subject: [PATCH 11/58] dict keys must not be in intended order, fixed this --- tools/console/pagan | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/console/pagan b/tools/console/pagan index ddecaf4..288f649 100644 --- a/tools/console/pagan +++ b/tools/console/pagan @@ -15,7 +15,8 @@ parser.add_argument("--output", help="save image to specific output path") parser.add_argument("--hash", default="MD5", help="use hash function, allowed %s, default %s" % - (generator.HASHES.values(), list(generator.HASHES.values())[0])) + (generator.HASHES.values(), + list(generator.HASHES.values())[0])) args = parser.parse_args() @@ -24,7 +25,11 @@ slogan = " ".join(args.input) if args.hash and args.hash not in generator.HASHES.values(): HASH = "" elif args.hash and args.hash in generator.HASHES.values(): - HASH = list(generator.HASHES.values()).index(args.hash) + # dict.keys() must not be right ordered + for key in generator.HASHES: + if args.hash == generator.HASHES[key]: + HASH = key + break if not args.output: args.show = True @@ -36,4 +41,4 @@ if args.show: if args.output: img.save(*os.path.split(args.output)) else: - img.save(os.getcwd(), filename=args.input[0]) \ No newline at end of file + img.save(os.getcwd(), filename=args.input[0]) From b530f122734fae3d44adaa05a652f0d0cd48ecd8 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 01:26:21 +0200 Subject: [PATCH 12/58] add simple test case for calling pagan --- test/test_bin_pagan.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 test/test_bin_pagan.py diff --git a/test/test_bin_pagan.py b/test/test_bin_pagan.py new file mode 100644 index 0000000..a0f32ae --- /dev/null +++ b/test/test_bin_pagan.py @@ -0,0 +1,10 @@ +from subprocess import PIPE +from subprocess import Popen + + +def test_simple_call(): + """check if calling the script simply works and yields no error""" + p = Popen(["pagan", "--output=/tmp/0101010101.png", "0101"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert not stderr From 6757ce9546d5580e9162fc1b078a83a286b5481b Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 01:37:48 +0200 Subject: [PATCH 13/58] test on failure, fals param or no input --- test/test_bin_pagan.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/test_bin_pagan.py b/test/test_bin_pagan.py index a0f32ae..a55396c 100644 --- a/test/test_bin_pagan.py +++ b/test/test_bin_pagan.py @@ -7,4 +7,21 @@ def test_simple_call(): p = Popen(["pagan", "--output=/tmp/0101010101.png", "0101"], stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() + assert stdout assert not stderr + + +def test_simple_call_err(): + p = Popen(["pagan", "--output=/tmp/0101010101.png", "--err", "0101"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert not stdout + assert stderr + + +def test_simple_call_err_no_input(): + p = Popen(["pagan", "--output=/tmp/0101010101.png"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert not stdout + assert stderr From a64ffd2e0e0d258e42363fef47567ace7aa57f78 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 02:19:14 +0200 Subject: [PATCH 14/58] cleanup index --- tools/webserver/webserver.py | 40 +++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index ce2c09c..580130a 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -38,17 +38,37 @@


-
Current SearchPrevious Search
-

{{slogan}} + + + + +
Current SearchPrevious Search

+

+ -

{{hist1}}

-

{{hist2}} + +

-

{{hist3}} + +

+

+{{slogan}} + +

+ +

+{{hist1}} + +

+{{hist2}} + +

+{{hist3}} +

""" @@ -107,6 +127,16 @@ def index(): hist2_hash = md5.hexdigest() md5.update(hist3) hist3_hash = md5.hexdigest() + + def add_wbr(s): + l = list(s) + for pos in range(0, len(l), 10): + l[pos] = l[pos] + " " + return s + return "".join(l) + hist1 = add_wbr(hist1) + hist2 = add_wbr(hist2) + hist3 = add_wbr(hist3) return template(TEMPLATEINDEX, slogan=slogan, hist1=hist1, hist2=hist2, hist3=hist3, sloganHash=slogan_hash, hist1Hash=hist1_hash, From 6729957656953ac0e78f8aff99dbd93f8e53c45b Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 02:20:04 +0200 Subject: [PATCH 15/58] rm wbr add --- tools/webserver/webserver.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index 580130a..8c0a4ce 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -128,15 +128,6 @@ def index(): md5.update(hist3) hist3_hash = md5.hexdigest() - def add_wbr(s): - l = list(s) - for pos in range(0, len(l), 10): - l[pos] = l[pos] + " " - return s - return "".join(l) - hist1 = add_wbr(hist1) - hist2 = add_wbr(hist2) - hist3 = add_wbr(hist3) return template(TEMPLATEINDEX, slogan=slogan, hist1=hist1, hist2=hist2, hist3=hist3, sloganHash=slogan_hash, hist1Hash=hist1_hash, From 5a942047d3741cede21634207562dedb6d0a48cf Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 13:07:38 +0200 Subject: [PATCH 16/58] default hash is md5 --- tools/console/pagan | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/console/pagan b/tools/console/pagan index 288f649..303749a 100644 --- a/tools/console/pagan +++ b/tools/console/pagan @@ -15,8 +15,7 @@ parser.add_argument("--output", help="save image to specific output path") parser.add_argument("--hash", default="MD5", help="use hash function, allowed %s, default %s" % - (generator.HASHES.values(), - list(generator.HASHES.values())[0])) + (generator.HASHES.values(), "MD5")) args = parser.parse_args() From 51e5647456d4a3fd5bd5636de7ffe63b69032ad4 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Fri, 14 Oct 2016 01:56:38 +0200 Subject: [PATCH 17/58] check for raise FalseHashError on buggy hash values as input for images --- tools/webserver/webserver.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index e6bde68..a4e59fe 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -110,7 +110,10 @@ def hashimage(hashvalue): """generate image by hash, usese tempfile :-/""" tmpf = tempfile.mkstemp(".png")[1] image = pagan.Avatar("") - image.img = pagan.generator.generate_by_hash(hashvalue) + try: + image.img = pagan.generator.generate_by_hash(hashvalue) + except pagan.generator.FalseHashError: + return "HashError, see log and check hash value, %s" % hashvalue image.save("/", tmpf) return static_file(tmpf, root="/") From af33f7d82c1a48af8ba0e6d41453c670a2f9615f Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Mon, 17 Oct 2016 11:53:50 +0200 Subject: [PATCH 18/58] allowed hash can contain upper and lower chars --- pagan/generator.py | 22 +++++++++++++--------- test/test_generator.py | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/pagan/generator.py b/pagan/generator.py index 3b3adc9..690c4f3 100644 --- a/pagan/generator.py +++ b/pagan/generator.py @@ -270,8 +270,10 @@ def setup_pixelmap(hashcode): def generate(str, alg): - """Generates an PIL image avatar based on the given - input String. Acts as the main accessor to pagan.""" + """Generates an PIL image avatar based on the given input String. + + Acts as the main accessor to pagan. + """ img = Image.new(IMAGE_MODE, IMAGE_SIZE, BACKGROUND_COLOR) hashcode = hash_input(str, alg) pixelmap = setup_pixelmap(hashcode) @@ -280,18 +282,20 @@ def generate(str, alg): def generate_by_hash(hashcode): - """Generates an PIL image avatar based on the given - hash String. Acts as the main accessor to pagan.""" + """Generates an PIL image avatar based on the given hash String. + + Acts as the main accessor to pagan. + """ img = Image.new(IMAGE_MODE, IMAGE_SIZE, BACKGROUND_COLOR) if len(hashcode) < 32: print ("hashcode must have lenght >= 32, %s" % hashcode) raise FalseHashError - allowed = "0123456789abcdef" - hashcheck = [c in allowed for c in hashcode] - if False in hashcheck: - print ("hashcode has not allowed structure %s" % hashcode) - raise FalseHashError + allowed = "0123456789ABCDEFabcdef" + for c in hashcode: + if c not in allowed: + print ("hashcode has not allowed structure %s" % hashcode) + raise FalseHashError pixelmap = setup_pixelmap(hashcode) draw_image(pixelmap, img) diff --git a/test/test_generator.py b/test/test_generator.py index a147b3c..b9a488e 100644 --- a/test/test_generator.py +++ b/test/test_generator.py @@ -22,4 +22,4 @@ def test_generate_by_hash(): img = pagan.generator.generate_by_hash(md5.hexdigest()[:2]) with pytest.raises(pagan.generator.FalseHashError): - img = pagan.generator.generate_by_hash(md5.hexdigest()+"A") + img = pagan.generator.generate_by_hash(md5.hexdigest()+"G") From 123785604da3017f1ef447ceeb821e0173412fb7 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Mon, 17 Oct 2016 11:59:26 +0200 Subject: [PATCH 19/58] fix merge relicts add test: save in new dir removed print statements changed 404 page more changes on 404 page of webserver --- test/test_pagan.py | 15 +++++++++++++++ tools/webserver/webserver.py | 11 ++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/test/test_pagan.py b/test/test_pagan.py index ad41ce6..f2ac4b5 100644 --- a/test/test_pagan.py +++ b/test/test_pagan.py @@ -1,4 +1,7 @@ # -*- coding: latin-1 -*- +import os +import random +import string import tempfile import pagan @@ -49,6 +52,17 @@ def test_save(): img0.save(tmpdir, tmpfile) +def test_save_new_dir(): + """save file in not existing directory""" + img0 = pagan.Avatar("1") + tmpdir = tempfile.gettempdir() + testdir = ''.join(random.SystemRandom().choice(string.digits) + for _ in range(12)) + testdir = os.path.join(tmpdir, testdir) + testfile = tempfile.mkstemp("", dir=tmpdir)[1].split("/")[-1] + img0.save(testdir, testfile) + + if __name__ == "__main__": test_create() test_diffrent_hash() @@ -56,3 +70,4 @@ def test_save(): test_show() test_change() test_save() + test_save_new_dir() diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index a4e59fe..664edf4 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -54,8 +54,12 @@ @error(404) def error404(code): """handle error 404 """ - return template('{{code}} Avatar not found. You may use this:', - code=code) + return """
+ + + I am the guard of this server and I am sorry to tell you:\ +

404 Avatar/Page not found.

Please go back to the \ + front door.""" @route('/') @@ -113,7 +117,8 @@ def hashimage(hashvalue): try: image.img = pagan.generator.generate_by_hash(hashvalue) except pagan.generator.FalseHashError: - return "HashError, see log and check hash value, %s" % hashvalue + return template("HashError, see log and check hash value, \ + {{hashvalue}}", hashvalue=hashvalue) image.save("/", tmpf) return static_file(tmpf, root="/") From ec7b43c0b70407eaddd07e2e1edcd3ea24075ce9 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 18 Oct 2016 21:41:28 +0200 Subject: [PATCH 20/58] use bottle app, changed 404 message --- tools/webserver/webserver.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index 664edf4..9bb15ee 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -1,10 +1,8 @@ """pagan webserver""" +from bottle import Bottle from bottle import debug -from bottle import error -from bottle import post from bottle import request from bottle import response -from bottle import route from bottle import run from bottle import static_file from bottle import template @@ -50,8 +48,10 @@

""" +app = Bottle() -@error(404) + +@app.error(404) def error404(code): """handle error 404 """ return """
@@ -62,8 +62,8 @@ def error404(code): front door.""" -@route('/') -@post('/') +@app.route('/') +@app.post('/') def index(): """main functionality of webserver""" default = ["pagan", "python", "avatar", "github"] @@ -109,7 +109,7 @@ def index(): hist2Hash=hist2_hash, hist3Hash=hist3_hash) -@route('/himage/') +@app.route('/himage/') def hashimage(hashvalue): """generate image by hash, usese tempfile :-/""" tmpf = tempfile.mkstemp(".png")[1] @@ -122,5 +122,15 @@ def hashimage(hashvalue): image.save("/", tmpf) return static_file(tmpf, root="/") + +@app.route('/coverage_exit') +def coverage_exit(): + """exit function for coverage""" + import os + import signal + if os.environ["COVERAGE_EXIT"] != "True": + return "" + os.kill(os.getpid(), signal.SIGTERM) + debug(True) -run(host='localhost', port=8080) +run(app, host='localhost', port=8080) From 80d8f2356f07162e6de46a5cd59aeb8b274ff35a Mon Sep 17 00:00:00 2001 From: Rohit Jha Date: Tue, 18 Oct 2016 20:10:09 -0700 Subject: [PATCH 21/58] Minor fixes to README --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f16a183..2c7e6a2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ pagan ===== -Welcome to the python avatar generator for absolute nerds. +Welcome to the Python avatar generator for absolute nerds. **Current version: 0.3.3** @@ -13,17 +13,17 @@ fill the void left by poorly pixelated images by yourself. Well, pagan tries to give back some of those nostalgic feelings by providing **identicons** in an oldschool look that are inspired from retro roleplaying adventure games. -Each string input will be hashed and generates a unique avatar image. The purpose +Each string input will be hashed and a unique avatar image is generated. The purpose of pagan is to use it for generating a user image in any web application. It is is meant to replace default user images when creating new accounts or to enhance -comment sections, e.g. visualizing the authors ip address or username. +comment sections, e.g. visualizing the author's IP address or username. **The software is currently under development and features the following functions:** * Process a given string to generate identicons with unique colors and gear. -* The hash function can be chosen from the ones included in pythons hashlib. +* The hash function can be chosen from the ones included in Python's hashlib. * Create the avatar image based on a given resolution. -* Pagan will map all virtual 16x16 Pixels to the real image size. +* Pagan will map all virtual 16x16 pixels to the real image size. * Expand pagan by adding new weapons or gear. * Enjoy the nostalgia! From 2fa9f7d30531319859b47d8982bc3091590a878a Mon Sep 17 00:00:00 2001 From: David Bothe Date: Wed, 19 Oct 2016 15:03:24 +0200 Subject: [PATCH 22/58] Modified README.md, expanded the pagan command line interface (CLI), fixed python3 issues with the CLI. --- README.md | 9 +++++---- pagan/hashgrinder.py | 2 +- setup.py | 4 ++-- tools/console/pagan | 15 ++++++++------- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 2c7e6a2..dbbbe3c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ pagan Welcome to the Python avatar generator for absolute nerds. -**Current version: 0.3.3** +**Current version: 0.4.3** View changes [here](CHANGELOG.md). @@ -102,6 +102,9 @@ sha512 | pagan.SHA512 ###Testing +To run the pagan tests, you need to install additional python modules. You can choose between pytest and tox. Configure +the tox.ini to test different python versions. + ####Using py.test ``` @@ -114,6 +117,4 @@ sha512 | pagan.SHA512 ``` >> pip install tox >> tox -``` - -you might have to adjust the python version in tox.ini to your needs. +``` \ No newline at end of file diff --git a/pagan/hashgrinder.py b/pagan/hashgrinder.py index 16bebb3..a362694 100644 --- a/pagan/hashgrinder.py +++ b/pagan/hashgrinder.py @@ -22,7 +22,7 @@ MAX_DECISION_VALUE = 16777215 # Set True to generate debug output in this module. -DEBUG = True +DEBUG = False WEAPONSTYLES = ['ONEHANDED_ONEHANDED', 'SHIELD_ONEHANDED', diff --git a/setup.py b/setup.py index 1afbf97..98cc1a0 100644 --- a/setup.py +++ b/setup.py @@ -5,9 +5,9 @@ packages=['pagan'], use_2to3=True, include_package_data=True, - version='0.3.3', + version='0.4.3', url='https://github.com/daboth/pagan', - download_url='https://github.com/daboth/pagan/tarball/0.3.3', + download_url='https://github.com/daboth/pagan/tarball/0.4.3', license='GPL', description='python avatar generator for absolute nerds', long_description=open('README.md').read(), diff --git a/tools/console/pagan b/tools/console/pagan index 30bc986..ddecaf4 100644 --- a/tools/console/pagan +++ b/tools/console/pagan @@ -1,6 +1,5 @@ #!/usr/bin/env python -"""call pagan from console -""" +"""pagan command line interface""" import argparse import os @@ -8,24 +7,24 @@ import pagan from pagan import generator parser = argparse.ArgumentParser() -parser.add_argument("slogan", nargs="+", +parser.add_argument("input", nargs="+", help="string, basis of avatar computing") parser.add_argument("--show", action="store_true", help="show avatar in external editor, default behavior") parser.add_argument("--output", - help="save image to output path") + help="save image to specific output path") parser.add_argument("--hash", default="MD5", help="use hash function, allowed %s, default %s" % - (generator.HASHES.values(), generator.HASHES.values()[0])) + (generator.HASHES.values(), list(generator.HASHES.values())[0])) args = parser.parse_args() -slogan = " ".join(args.slogan) +slogan = " ".join(args.input) if args.hash and args.hash not in generator.HASHES.values(): HASH = "" elif args.hash and args.hash in generator.HASHES.values(): - HASH = generator.HASHES.values().index(args.hash) + HASH = list(generator.HASHES.values()).index(args.hash) if not args.output: args.show = True @@ -36,3 +35,5 @@ if args.show: if args.output: img.save(*os.path.split(args.output)) +else: + img.save(os.getcwd(), filename=args.input[0]) \ No newline at end of file From 43340d2c1f0e7f507fc1732c054024b003477f72 Mon Sep 17 00:00:00 2001 From: David Bothe Date: Wed, 19 Oct 2016 15:26:01 +0200 Subject: [PATCH 23/58] Updated CHANGELOG.md and README.md --- CHANGELOG.md | 23 ++++++----------------- README.md | 40 +++++++++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90a8938..104abb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,56 +1,45 @@ +### 0.4.3 + + * Added simple webserver (early state, unstable) to use pagan as a web application. + * Added pagan command line interface to generate avatars from your terminal. + * Added pytest and tox tests. + ### 0.3.3 -**Major changes** * Resolved issue with newer versions of pillow. * Resolved issue with python 3 imports. * Resolved issue with python 3 hash encoding. - -**Minor changes** - * Updated Readme. ### 0.3.2 -**Major changes** * Implementing the Avatar class in pagan.py as the main accessor. * Overall restructure for an object oriented approach. * Avatar objects can now be instantiated and manipulated before drawing. * Hash functions can now be omitted on Avatar creation by using simple constants (e.g., pagan.SHA512). * Generator paths are now OS independent. - -**Minor changes** - * Comment cleanup on some methods. * Constants are now initialised in __init__.py ### 0.2.3 -**Major changes** * Refactoring of the main filename from pagan.py to generator.py * Added pathhandling for correct template reading to import pagan as a installed module. - -**Minor changes** - * Hashfunction accessors are now stored in a dictionary. * When run as main, the predefined samples run all available hash algorithms. * Added a setup script and manifest to publish on pypi. ### 0.2.2 -**Minor changes** * Refactoring * Cleanup ### 0.2.1 -**Major changes** * Modified pagan to generate hashes from arbitrary input strings. * Avatar creation is now fully based on a hash in hexadecimal form instead of IPv4 addresses. * Supports all hash algorithms that are included in pythons hashlib. * Can still process IPv4 addresses, they are also hashed now. Same applies for IPv6 addresses. * Features more colors. Each part of the gear is now painted uniquely due to the potential of hashes. - -**Minor changes** - * Did some refactoring. * Added documentation. diff --git a/README.md b/README.md index dbbbe3c..19e1651 100644 --- a/README.md +++ b/README.md @@ -43,18 +43,18 @@ hash me if you can | ![hash me if you can](/images/hash%20me%20if%20you%20can.pn Clone this repository: ``` -git clone https://github.com/daboth/pagan.git +>> git clone https://github.com/daboth/pagan.git ``` and install manually: ``` -python setup.py install +>> python setup.py install ``` or install with pip: ``` -pip install pagan +>> pip install pagan ``` -###Usage example: +###Python usage example: ```python # Import the pagan module. import pagan @@ -88,6 +88,37 @@ img.save(outpath, filename) img.change('new input', pagan.SHA256) ``` +###Command Line Interface + +With the pagan command line interface you can generate avatars without writing python scripts. +``` +>> pagan [-h] [--show] [--output OUTPUT] [--hash HASH] input [input ...] +``` + +Simply typing: +``` +>> pagan hello +``` +will generate an avatar from the string 'hello' and save it in your current working directory. For more information, +use the help parameter. +``` +>> pagan -h +``` + +###Webserver + +Pagan can be run in a simple demo webserver application. To access the pagan demo, you need to run the script +from the webserver directory. Beware: This is a demo application and it will fill your temp directory with pagan generated +image files. Do not run in production. +``` +>> cd /tools/webserver/ +>> python webserver.py +``` +The webserver will serve on your localhost at port 8080. Open this adress in your browser window: +``` +http://127.0.0.1:8080/ +``` + ###Supported Hashes Hash | Constant @@ -99,7 +130,6 @@ sha256 | pagan.SHA256 sha384 | pagan.SHA384 sha512 | pagan.SHA512 - ###Testing To run the pagan tests, you need to install additional python modules. You can choose between pytest and tox. Configure From 4fe0d73b774f359c81e85b3bd3113cbf303379af Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 00:32:29 +0200 Subject: [PATCH 24/58] minor changes --- tools/webserver/webserver.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index 9bb15ee..3d88027 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -132,5 +132,6 @@ def coverage_exit(): return "" os.kill(os.getpid(), signal.SIGTERM) -debug(True) -run(app, host='localhost', port=8080) +if __name__ == "__main__": + debug(True) + run(app, host='localhost', port=8080) From 8f8aad56f1aa114589165673129ad47735390741 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 01:25:25 +0200 Subject: [PATCH 25/58] dict keys must not be in intended order, fixed this --- tools/console/pagan | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/console/pagan b/tools/console/pagan index ddecaf4..288f649 100644 --- a/tools/console/pagan +++ b/tools/console/pagan @@ -15,7 +15,8 @@ parser.add_argument("--output", help="save image to specific output path") parser.add_argument("--hash", default="MD5", help="use hash function, allowed %s, default %s" % - (generator.HASHES.values(), list(generator.HASHES.values())[0])) + (generator.HASHES.values(), + list(generator.HASHES.values())[0])) args = parser.parse_args() @@ -24,7 +25,11 @@ slogan = " ".join(args.input) if args.hash and args.hash not in generator.HASHES.values(): HASH = "" elif args.hash and args.hash in generator.HASHES.values(): - HASH = list(generator.HASHES.values()).index(args.hash) + # dict.keys() must not be right ordered + for key in generator.HASHES: + if args.hash == generator.HASHES[key]: + HASH = key + break if not args.output: args.show = True @@ -36,4 +41,4 @@ if args.show: if args.output: img.save(*os.path.split(args.output)) else: - img.save(os.getcwd(), filename=args.input[0]) \ No newline at end of file + img.save(os.getcwd(), filename=args.input[0]) From f78813e8ffbf6a2e9986689f836c66bd7d2df9a6 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 01:26:21 +0200 Subject: [PATCH 26/58] add simple test case for calling pagan --- test/test_bin_pagan.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 test/test_bin_pagan.py diff --git a/test/test_bin_pagan.py b/test/test_bin_pagan.py new file mode 100644 index 0000000..a0f32ae --- /dev/null +++ b/test/test_bin_pagan.py @@ -0,0 +1,10 @@ +from subprocess import PIPE +from subprocess import Popen + + +def test_simple_call(): + """check if calling the script simply works and yields no error""" + p = Popen(["pagan", "--output=/tmp/0101010101.png", "0101"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert not stderr From 1f5663b0c3244d5e2c95b1e199a4652373e124ec Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 01:37:48 +0200 Subject: [PATCH 27/58] test on failure, fals param or no input --- test/test_bin_pagan.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/test_bin_pagan.py b/test/test_bin_pagan.py index a0f32ae..a55396c 100644 --- a/test/test_bin_pagan.py +++ b/test/test_bin_pagan.py @@ -7,4 +7,21 @@ def test_simple_call(): p = Popen(["pagan", "--output=/tmp/0101010101.png", "0101"], stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() + assert stdout assert not stderr + + +def test_simple_call_err(): + p = Popen(["pagan", "--output=/tmp/0101010101.png", "--err", "0101"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert not stdout + assert stderr + + +def test_simple_call_err_no_input(): + p = Popen(["pagan", "--output=/tmp/0101010101.png"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert not stdout + assert stderr From 02368071f02c8844730a10b800f38d57004e641e Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 02:19:14 +0200 Subject: [PATCH 28/58] cleanup index --- tools/webserver/webserver.py | 40 +++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index 3d88027..abeda5a 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -34,17 +34,37 @@

-
Current SearchPrevious Search
-

{{slogan}} + + + + +
Current SearchPrevious Search

+

+ -

{{hist1}}

-

{{hist2}} + +

-

{{hist3}} + +

+

+{{slogan}} + +

+ +

+{{hist1}} + +

+{{hist2}} + +

+{{hist3}} +

""" @@ -103,6 +123,16 @@ def index(): hist2_hash = md5.hexdigest() md5.update(hist3) hist3_hash = md5.hexdigest() + + def add_wbr(s): + l = list(s) + for pos in range(0, len(l), 10): + l[pos] = l[pos] + " " + return s + return "".join(l) + hist1 = add_wbr(hist1) + hist2 = add_wbr(hist2) + hist3 = add_wbr(hist3) return template(TEMPLATEINDEX, slogan=slogan, hist1=hist1, hist2=hist2, hist3=hist3, sloganHash=slogan_hash, hist1Hash=hist1_hash, From bd30ee96502601ec451e719f3eab5c98fa6f35b1 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 02:20:04 +0200 Subject: [PATCH 29/58] rm wbr add --- tools/webserver/webserver.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index abeda5a..ba45e96 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -124,15 +124,6 @@ def index(): md5.update(hist3) hist3_hash = md5.hexdigest() - def add_wbr(s): - l = list(s) - for pos in range(0, len(l), 10): - l[pos] = l[pos] + " " - return s - return "".join(l) - hist1 = add_wbr(hist1) - hist2 = add_wbr(hist2) - hist3 = add_wbr(hist3) return template(TEMPLATEINDEX, slogan=slogan, hist1=hist1, hist2=hist2, hist3=hist3, sloganHash=slogan_hash, hist1Hash=hist1_hash, From c19dc426f5726b284b5e605bf4faa6bfbff11168 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 25 Oct 2016 12:31:35 +0200 Subject: [PATCH 30/58] fixed host in webserver.py --- tools/webserver/webserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index 8c0a4ce..ce307d7 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -159,4 +159,4 @@ def coverage_exit(): if __name__ == "__main__": debug(True) - run(app, host='localhost', port=8080) + run(app, host='0.0.0.0', port=8080) From 2b982167c252dc1d78d824a57a89eb1790014f41 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 25 Oct 2016 16:42:34 +0200 Subject: [PATCH 31/58] running pagan webservice on docker --- Dockerfile.webserver | 25 +++++++++++++++++++++++++ Readme_docker.md | 29 +++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 Dockerfile.webserver create mode 100644 Readme_docker.md diff --git a/Dockerfile.webserver b/Dockerfile.webserver new file mode 100644 index 0000000..bcb72e2 --- /dev/null +++ b/Dockerfile.webserver @@ -0,0 +1,25 @@ +# A super-simple "hello world" server that exposes port 8080 +# +# VERSION 0.1.0 +FROM debian +MAINTAINER me + +# create user +RUN groupadd web +RUN useradd -d /home/bottle -m bottle + +# make sure sources are up to date +RUN apt-get update +RUN apt-get upgrade -y + +# install pip and hello-world server requirements +RUN apt-get install python-pip -y --no-install-recommends +RUN apt-get install python-pil -y --no-install-recommends +RUN pip install bottle +RUN pip install pagan +ADD ./ /home/bottle/pagan/ + +# in case you'd prefer to use links, expose the port +EXPOSE 8080 +ENTRYPOINT ["/usr/bin/python", "/home/bottle/pagan/tools/webserver/webserver.py"] +USER bottle diff --git a/Readme_docker.md b/Readme_docker.md new file mode 100644 index 0000000..54c3f08 --- /dev/null +++ b/Readme_docker.md @@ -0,0 +1,29 @@ +# using pagan and docker + +## pagan webserver + +Running pagan webserver on port 8080. + +### Build: + +docker build -t paganweb -f Dockerfile.webserver . + +### Run: + +docker run -d -p 8080:8080 paganweb + +or: + +docker run --rm -p 8080:8080 paganweb + +### Stop: + +docker stop + +### Debug: + +docker exec -it bash + +### Docker ID: + +docker ps From e0afc376c898938fe7ebc453f91fc4556ba67d4d Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 25 Oct 2016 17:02:26 +0200 Subject: [PATCH 32/58] webserver: error404 -> template --- tools/webserver/webserver.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index ce307d7..dc82134 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -14,6 +14,10 @@ # TODO() Long input on main page destroys page # TODO() Better Layout on main page # TODO() Beautify 404 Error +# TODO() Select Hash Function +# TODO() Store choosen Hash in Cookie +# TODO() Sent Image on the fly +# TODO() Seperate Template from code logging.warning("\n%s\n\ early work in progress, try at your own risk\n\ @@ -78,12 +82,8 @@ @app.error(404) def error404(code): """handle error 404 """ - return """
- - - I am the guard of this server and I am sorry to tell you:\ -

404 Avatar/Page not found.

Please go back to the \ - front door.""" + return template("error.template", + guardcode="01234567890abcdef01234567890abcdef") @app.route('/') From 0d50d6baf4464751c5ec549d2a328139c1eb6035 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 25 Oct 2016 17:03:29 +0200 Subject: [PATCH 33/58] pagan console, removed / and .. from filename --- tools/console/pagan | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/console/pagan b/tools/console/pagan index 303749a..b4d8309 100644 --- a/tools/console/pagan +++ b/tools/console/pagan @@ -40,4 +40,5 @@ if args.show: if args.output: img.save(*os.path.split(args.output)) else: - img.save(os.getcwd(), filename=args.input[0]) + img.save(os.getcwd(), + filename=args.input[0].replace("/", "").replace("..", "")) From f98310eb42d7b34e539751705a72291810bec462 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 8 Nov 2016 00:59:12 +0100 Subject: [PATCH 34/58] pagan webserver rep --- Dockerfile.webserver | 25 ------ tools/webserver/Readme.md | 2 - tools/webserver/webserver.py | 162 ----------------------------------- 3 files changed, 189 deletions(-) delete mode 100644 Dockerfile.webserver delete mode 100644 tools/webserver/Readme.md delete mode 100644 tools/webserver/webserver.py diff --git a/Dockerfile.webserver b/Dockerfile.webserver deleted file mode 100644 index bcb72e2..0000000 --- a/Dockerfile.webserver +++ /dev/null @@ -1,25 +0,0 @@ -# A super-simple "hello world" server that exposes port 8080 -# -# VERSION 0.1.0 -FROM debian -MAINTAINER me - -# create user -RUN groupadd web -RUN useradd -d /home/bottle -m bottle - -# make sure sources are up to date -RUN apt-get update -RUN apt-get upgrade -y - -# install pip and hello-world server requirements -RUN apt-get install python-pip -y --no-install-recommends -RUN apt-get install python-pil -y --no-install-recommends -RUN pip install bottle -RUN pip install pagan -ADD ./ /home/bottle/pagan/ - -# in case you'd prefer to use links, expose the port -EXPOSE 8080 -ENTRYPOINT ["/usr/bin/python", "/home/bottle/pagan/tools/webserver/webserver.py"] -USER bottle diff --git a/tools/webserver/Readme.md b/tools/webserver/Readme.md deleted file mode 100644 index 501ca58..0000000 --- a/tools/webserver/Readme.md +++ /dev/null @@ -1,2 +0,0 @@ -requirement: - bottle diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py deleted file mode 100644 index dc82134..0000000 --- a/tools/webserver/webserver.py +++ /dev/null @@ -1,162 +0,0 @@ -"""pagan webserver""" -from bottle import Bottle -from bottle import debug -from bottle import request -from bottle import response -from bottle import run -from bottle import static_file -from bottle import template -import hashlib -import logging -import pagan -import tempfile - -# TODO() Long input on main page destroys page -# TODO() Better Layout on main page -# TODO() Beautify 404 Error -# TODO() Select Hash Function -# TODO() Store choosen Hash in Cookie -# TODO() Sent Image on the fly -# TODO() Seperate Template from code - -logging.warning("\n%s\n\ -early work in progress, try at your own risk\n\ -this will pollute your temp dir!\n\ -%s" % ("= "*32, "= "*32)) - -TEMPLATEINDEX = """ -

-

pagan

-
-Welcome to the python avatar generator for absolute nerds. -

Generate your own Avatar. -

-

-
-
- - - - -
Current SearchPrevious Search
-

- -

- - -

- -

-

- -

-

-

-{{slogan}} - -

- -

-{{hist1}} - -

-{{hist2}} - -

-{{hist3}} - -

-""" - -app = Bottle() - - -@app.error(404) -def error404(code): - """handle error 404 """ - return template("error.template", - guardcode="01234567890abcdef01234567890abcdef") - - -@app.route('/') -@app.post('/') -def index(): - """main functionality of webserver""" - default = ["pagan", "python", "avatar", "github"] - slogan = request.forms.get("slogan") - - if not slogan: - if request.get_cookie("hist1"): - slogan = request.get_cookie("hist1") - else: - slogan = "pagan" - - if not request.get_cookie("hist1"): - hist1, hist2, hist3, hist4 = default[:] - else: - hist1 = request.get_cookie("hist1") - hist2 = request.get_cookie("hist2") - hist3 = request.get_cookie("hist3") - hist4 = request.get_cookie("hist4") - - if slogan in (hist1, hist2, hist3, hist4): - history = [hist1, hist2, hist3, hist4] - history.remove(slogan) - hist1, hist2, hist3 = history[0], history[1], history[2] - - response.set_cookie("hist1", slogan, max_age=60*60*24*30, httponly=True) - response.set_cookie("hist2", hist1, max_age=60*60*24*30, httponly=True) - response.set_cookie("hist3", hist2, max_age=60*60*24*30, httponly=True) - response.set_cookie("hist4", hist3, max_age=60*60*24*30, httponly=True) - # slogan, hist1, hist2, hist3 = escape(slogan), escape(hist1),\ - # escape(hist2), escape(hist3) - md5 = hashlib.md5() - md5.update(slogan) - slogan_hash = md5.hexdigest() - md5.update(hist1) - hist1_hash = md5.hexdigest() - md5.update(hist2) - hist2_hash = md5.hexdigest() - md5.update(hist3) - hist3_hash = md5.hexdigest() - - return template(TEMPLATEINDEX, slogan=slogan, - hist1=hist1, hist2=hist2, hist3=hist3, - sloganHash=slogan_hash, hist1Hash=hist1_hash, - hist2Hash=hist2_hash, hist3Hash=hist3_hash) - - -@app.route('/himage/') -def hashimage(hashvalue): - """generate image by hash, usese tempfile :-/""" - tmpf = tempfile.mkstemp(".png")[1] - image = pagan.Avatar("") - try: - image.img = pagan.generator.generate_by_hash(hashvalue) - except pagan.generator.FalseHashError: - return template("HashError, see log and check hash value, \ - {{hashvalue}}", hashvalue=hashvalue) - image.save("/", tmpf) - return static_file(tmpf, root="/") - - -@app.route('/coverage_exit') -def coverage_exit(): - """exit function for coverage""" - import os - import signal - if os.environ["COVERAGE_EXIT"] != "True": - return "" - os.kill(os.getpid(), signal.SIGTERM) - -if __name__ == "__main__": - debug(True) - run(app, host='0.0.0.0', port=8080) From fb0dcbf4a101e3a2674ed169de1c86fdfac48f5e Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 8 Nov 2016 01:05:12 +0100 Subject: [PATCH 35/58] more prepare new git rep --- Readme_docker.md | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 Readme_docker.md diff --git a/Readme_docker.md b/Readme_docker.md deleted file mode 100644 index 54c3f08..0000000 --- a/Readme_docker.md +++ /dev/null @@ -1,29 +0,0 @@ -# using pagan and docker - -## pagan webserver - -Running pagan webserver on port 8080. - -### Build: - -docker build -t paganweb -f Dockerfile.webserver . - -### Run: - -docker run -d -p 8080:8080 paganweb - -or: - -docker run --rm -p 8080:8080 paganweb - -### Stop: - -docker stop - -### Debug: - -docker exec -it bash - -### Docker ID: - -docker ps From 4d33f05e65df67eaf15a655977b1a11fec5970f0 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Fri, 14 Oct 2016 01:56:38 +0200 Subject: [PATCH 36/58] check for raise FalseHashError on buggy hash values as input for images --- tools/webserver/webserver.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index e6bde68..a4e59fe 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -110,7 +110,10 @@ def hashimage(hashvalue): """generate image by hash, usese tempfile :-/""" tmpf = tempfile.mkstemp(".png")[1] image = pagan.Avatar("") - image.img = pagan.generator.generate_by_hash(hashvalue) + try: + image.img = pagan.generator.generate_by_hash(hashvalue) + except pagan.generator.FalseHashError: + return "HashError, see log and check hash value, %s" % hashvalue image.save("/", tmpf) return static_file(tmpf, root="/") From 5ea3e563c946cc0cf176954db359de88e7079996 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Mon, 17 Oct 2016 11:53:50 +0200 Subject: [PATCH 37/58] allowed hash can contain upper and lower chars --- pagan/generator.py | 22 +++++++++++++--------- test/test_generator.py | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/pagan/generator.py b/pagan/generator.py index 3b3adc9..690c4f3 100644 --- a/pagan/generator.py +++ b/pagan/generator.py @@ -270,8 +270,10 @@ def setup_pixelmap(hashcode): def generate(str, alg): - """Generates an PIL image avatar based on the given - input String. Acts as the main accessor to pagan.""" + """Generates an PIL image avatar based on the given input String. + + Acts as the main accessor to pagan. + """ img = Image.new(IMAGE_MODE, IMAGE_SIZE, BACKGROUND_COLOR) hashcode = hash_input(str, alg) pixelmap = setup_pixelmap(hashcode) @@ -280,18 +282,20 @@ def generate(str, alg): def generate_by_hash(hashcode): - """Generates an PIL image avatar based on the given - hash String. Acts as the main accessor to pagan.""" + """Generates an PIL image avatar based on the given hash String. + + Acts as the main accessor to pagan. + """ img = Image.new(IMAGE_MODE, IMAGE_SIZE, BACKGROUND_COLOR) if len(hashcode) < 32: print ("hashcode must have lenght >= 32, %s" % hashcode) raise FalseHashError - allowed = "0123456789abcdef" - hashcheck = [c in allowed for c in hashcode] - if False in hashcheck: - print ("hashcode has not allowed structure %s" % hashcode) - raise FalseHashError + allowed = "0123456789ABCDEFabcdef" + for c in hashcode: + if c not in allowed: + print ("hashcode has not allowed structure %s" % hashcode) + raise FalseHashError pixelmap = setup_pixelmap(hashcode) draw_image(pixelmap, img) diff --git a/test/test_generator.py b/test/test_generator.py index a147b3c..b9a488e 100644 --- a/test/test_generator.py +++ b/test/test_generator.py @@ -22,4 +22,4 @@ def test_generate_by_hash(): img = pagan.generator.generate_by_hash(md5.hexdigest()[:2]) with pytest.raises(pagan.generator.FalseHashError): - img = pagan.generator.generate_by_hash(md5.hexdigest()+"A") + img = pagan.generator.generate_by_hash(md5.hexdigest()+"G") From 474636c05a3c9c9893d489808190e273106228d0 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Mon, 17 Oct 2016 11:59:26 +0200 Subject: [PATCH 38/58] fix merge relicts add test: save in new dir removed print statements changed 404 page more changes on 404 page of webserver --- test/test_pagan.py | 15 +++++++++++++++ tools/webserver/webserver.py | 11 ++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/test/test_pagan.py b/test/test_pagan.py index ad41ce6..f2ac4b5 100644 --- a/test/test_pagan.py +++ b/test/test_pagan.py @@ -1,4 +1,7 @@ # -*- coding: latin-1 -*- +import os +import random +import string import tempfile import pagan @@ -49,6 +52,17 @@ def test_save(): img0.save(tmpdir, tmpfile) +def test_save_new_dir(): + """save file in not existing directory""" + img0 = pagan.Avatar("1") + tmpdir = tempfile.gettempdir() + testdir = ''.join(random.SystemRandom().choice(string.digits) + for _ in range(12)) + testdir = os.path.join(tmpdir, testdir) + testfile = tempfile.mkstemp("", dir=tmpdir)[1].split("/")[-1] + img0.save(testdir, testfile) + + if __name__ == "__main__": test_create() test_diffrent_hash() @@ -56,3 +70,4 @@ def test_save(): test_show() test_change() test_save() + test_save_new_dir() diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index a4e59fe..664edf4 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -54,8 +54,12 @@ @error(404) def error404(code): """handle error 404 """ - return template('{{code}} Avatar not found. You may use this:', - code=code) + return """
+ + + I am the guard of this server and I am sorry to tell you:\ +

404 Avatar/Page not found.

Please go back to the \ + front door.""" @route('/') @@ -113,7 +117,8 @@ def hashimage(hashvalue): try: image.img = pagan.generator.generate_by_hash(hashvalue) except pagan.generator.FalseHashError: - return "HashError, see log and check hash value, %s" % hashvalue + return template("HashError, see log and check hash value, \ + {{hashvalue}}", hashvalue=hashvalue) image.save("/", tmpf) return static_file(tmpf, root="/") From 9dbb81eac17557bacfa18635ed25834e971b0183 Mon Sep 17 00:00:00 2001 From: Rohit Jha Date: Tue, 18 Oct 2016 20:10:09 -0700 Subject: [PATCH 39/58] Minor fixes to README --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f16a183..2c7e6a2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ pagan ===== -Welcome to the python avatar generator for absolute nerds. +Welcome to the Python avatar generator for absolute nerds. **Current version: 0.3.3** @@ -13,17 +13,17 @@ fill the void left by poorly pixelated images by yourself. Well, pagan tries to give back some of those nostalgic feelings by providing **identicons** in an oldschool look that are inspired from retro roleplaying adventure games. -Each string input will be hashed and generates a unique avatar image. The purpose +Each string input will be hashed and a unique avatar image is generated. The purpose of pagan is to use it for generating a user image in any web application. It is is meant to replace default user images when creating new accounts or to enhance -comment sections, e.g. visualizing the authors ip address or username. +comment sections, e.g. visualizing the author's IP address or username. **The software is currently under development and features the following functions:** * Process a given string to generate identicons with unique colors and gear. -* The hash function can be chosen from the ones included in pythons hashlib. +* The hash function can be chosen from the ones included in Python's hashlib. * Create the avatar image based on a given resolution. -* Pagan will map all virtual 16x16 Pixels to the real image size. +* Pagan will map all virtual 16x16 pixels to the real image size. * Expand pagan by adding new weapons or gear. * Enjoy the nostalgia! From 3397885ba1e21f0b6ff9e019c029399d98268e0f Mon Sep 17 00:00:00 2001 From: David Bothe Date: Wed, 19 Oct 2016 15:03:24 +0200 Subject: [PATCH 40/58] Modified README.md, expanded the pagan command line interface (CLI), fixed python3 issues with the CLI. --- README.md | 9 +++++---- pagan/hashgrinder.py | 2 +- setup.py | 4 ++-- tools/console/pagan | 15 ++++++++------- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 2c7e6a2..dbbbe3c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ pagan Welcome to the Python avatar generator for absolute nerds. -**Current version: 0.3.3** +**Current version: 0.4.3** View changes [here](CHANGELOG.md). @@ -102,6 +102,9 @@ sha512 | pagan.SHA512 ###Testing +To run the pagan tests, you need to install additional python modules. You can choose between pytest and tox. Configure +the tox.ini to test different python versions. + ####Using py.test ``` @@ -114,6 +117,4 @@ sha512 | pagan.SHA512 ``` >> pip install tox >> tox -``` - -you might have to adjust the python version in tox.ini to your needs. +``` \ No newline at end of file diff --git a/pagan/hashgrinder.py b/pagan/hashgrinder.py index 16bebb3..a362694 100644 --- a/pagan/hashgrinder.py +++ b/pagan/hashgrinder.py @@ -22,7 +22,7 @@ MAX_DECISION_VALUE = 16777215 # Set True to generate debug output in this module. -DEBUG = True +DEBUG = False WEAPONSTYLES = ['ONEHANDED_ONEHANDED', 'SHIELD_ONEHANDED', diff --git a/setup.py b/setup.py index 1afbf97..98cc1a0 100644 --- a/setup.py +++ b/setup.py @@ -5,9 +5,9 @@ packages=['pagan'], use_2to3=True, include_package_data=True, - version='0.3.3', + version='0.4.3', url='https://github.com/daboth/pagan', - download_url='https://github.com/daboth/pagan/tarball/0.3.3', + download_url='https://github.com/daboth/pagan/tarball/0.4.3', license='GPL', description='python avatar generator for absolute nerds', long_description=open('README.md').read(), diff --git a/tools/console/pagan b/tools/console/pagan index 30bc986..ddecaf4 100644 --- a/tools/console/pagan +++ b/tools/console/pagan @@ -1,6 +1,5 @@ #!/usr/bin/env python -"""call pagan from console -""" +"""pagan command line interface""" import argparse import os @@ -8,24 +7,24 @@ import pagan from pagan import generator parser = argparse.ArgumentParser() -parser.add_argument("slogan", nargs="+", +parser.add_argument("input", nargs="+", help="string, basis of avatar computing") parser.add_argument("--show", action="store_true", help="show avatar in external editor, default behavior") parser.add_argument("--output", - help="save image to output path") + help="save image to specific output path") parser.add_argument("--hash", default="MD5", help="use hash function, allowed %s, default %s" % - (generator.HASHES.values(), generator.HASHES.values()[0])) + (generator.HASHES.values(), list(generator.HASHES.values())[0])) args = parser.parse_args() -slogan = " ".join(args.slogan) +slogan = " ".join(args.input) if args.hash and args.hash not in generator.HASHES.values(): HASH = "" elif args.hash and args.hash in generator.HASHES.values(): - HASH = generator.HASHES.values().index(args.hash) + HASH = list(generator.HASHES.values()).index(args.hash) if not args.output: args.show = True @@ -36,3 +35,5 @@ if args.show: if args.output: img.save(*os.path.split(args.output)) +else: + img.save(os.getcwd(), filename=args.input[0]) \ No newline at end of file From 168a3dba34a602a7d2709019fa6f3bfc4d1805d9 Mon Sep 17 00:00:00 2001 From: David Bothe Date: Wed, 19 Oct 2016 15:26:01 +0200 Subject: [PATCH 41/58] Updated CHANGELOG.md and README.md --- CHANGELOG.md | 23 ++++++----------------- README.md | 40 +++++++++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90a8938..104abb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,56 +1,45 @@ +### 0.4.3 + + * Added simple webserver (early state, unstable) to use pagan as a web application. + * Added pagan command line interface to generate avatars from your terminal. + * Added pytest and tox tests. + ### 0.3.3 -**Major changes** * Resolved issue with newer versions of pillow. * Resolved issue with python 3 imports. * Resolved issue with python 3 hash encoding. - -**Minor changes** - * Updated Readme. ### 0.3.2 -**Major changes** * Implementing the Avatar class in pagan.py as the main accessor. * Overall restructure for an object oriented approach. * Avatar objects can now be instantiated and manipulated before drawing. * Hash functions can now be omitted on Avatar creation by using simple constants (e.g., pagan.SHA512). * Generator paths are now OS independent. - -**Minor changes** - * Comment cleanup on some methods. * Constants are now initialised in __init__.py ### 0.2.3 -**Major changes** * Refactoring of the main filename from pagan.py to generator.py * Added pathhandling for correct template reading to import pagan as a installed module. - -**Minor changes** - * Hashfunction accessors are now stored in a dictionary. * When run as main, the predefined samples run all available hash algorithms. * Added a setup script and manifest to publish on pypi. ### 0.2.2 -**Minor changes** * Refactoring * Cleanup ### 0.2.1 -**Major changes** * Modified pagan to generate hashes from arbitrary input strings. * Avatar creation is now fully based on a hash in hexadecimal form instead of IPv4 addresses. * Supports all hash algorithms that are included in pythons hashlib. * Can still process IPv4 addresses, they are also hashed now. Same applies for IPv6 addresses. * Features more colors. Each part of the gear is now painted uniquely due to the potential of hashes. - -**Minor changes** - * Did some refactoring. * Added documentation. diff --git a/README.md b/README.md index dbbbe3c..19e1651 100644 --- a/README.md +++ b/README.md @@ -43,18 +43,18 @@ hash me if you can | ![hash me if you can](/images/hash%20me%20if%20you%20can.pn Clone this repository: ``` -git clone https://github.com/daboth/pagan.git +>> git clone https://github.com/daboth/pagan.git ``` and install manually: ``` -python setup.py install +>> python setup.py install ``` or install with pip: ``` -pip install pagan +>> pip install pagan ``` -###Usage example: +###Python usage example: ```python # Import the pagan module. import pagan @@ -88,6 +88,37 @@ img.save(outpath, filename) img.change('new input', pagan.SHA256) ``` +###Command Line Interface + +With the pagan command line interface you can generate avatars without writing python scripts. +``` +>> pagan [-h] [--show] [--output OUTPUT] [--hash HASH] input [input ...] +``` + +Simply typing: +``` +>> pagan hello +``` +will generate an avatar from the string 'hello' and save it in your current working directory. For more information, +use the help parameter. +``` +>> pagan -h +``` + +###Webserver + +Pagan can be run in a simple demo webserver application. To access the pagan demo, you need to run the script +from the webserver directory. Beware: This is a demo application and it will fill your temp directory with pagan generated +image files. Do not run in production. +``` +>> cd /tools/webserver/ +>> python webserver.py +``` +The webserver will serve on your localhost at port 8080. Open this adress in your browser window: +``` +http://127.0.0.1:8080/ +``` + ###Supported Hashes Hash | Constant @@ -99,7 +130,6 @@ sha256 | pagan.SHA256 sha384 | pagan.SHA384 sha512 | pagan.SHA512 - ###Testing To run the pagan tests, you need to install additional python modules. You can choose between pytest and tox. Configure From 01e5a6043be65671d5a1327e3b2d11c26bbc0373 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 01:25:25 +0200 Subject: [PATCH 42/58] dict keys must not be in intended order, fixed this --- tools/console/pagan | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/console/pagan b/tools/console/pagan index ddecaf4..288f649 100644 --- a/tools/console/pagan +++ b/tools/console/pagan @@ -15,7 +15,8 @@ parser.add_argument("--output", help="save image to specific output path") parser.add_argument("--hash", default="MD5", help="use hash function, allowed %s, default %s" % - (generator.HASHES.values(), list(generator.HASHES.values())[0])) + (generator.HASHES.values(), + list(generator.HASHES.values())[0])) args = parser.parse_args() @@ -24,7 +25,11 @@ slogan = " ".join(args.input) if args.hash and args.hash not in generator.HASHES.values(): HASH = "" elif args.hash and args.hash in generator.HASHES.values(): - HASH = list(generator.HASHES.values()).index(args.hash) + # dict.keys() must not be right ordered + for key in generator.HASHES: + if args.hash == generator.HASHES[key]: + HASH = key + break if not args.output: args.show = True @@ -36,4 +41,4 @@ if args.show: if args.output: img.save(*os.path.split(args.output)) else: - img.save(os.getcwd(), filename=args.input[0]) \ No newline at end of file + img.save(os.getcwd(), filename=args.input[0]) From 2dc804ac4af910066b528e9703da9d0877687f29 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 01:26:21 +0200 Subject: [PATCH 43/58] add simple test case for calling pagan --- test/test_bin_pagan.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 test/test_bin_pagan.py diff --git a/test/test_bin_pagan.py b/test/test_bin_pagan.py new file mode 100644 index 0000000..a0f32ae --- /dev/null +++ b/test/test_bin_pagan.py @@ -0,0 +1,10 @@ +from subprocess import PIPE +from subprocess import Popen + + +def test_simple_call(): + """check if calling the script simply works and yields no error""" + p = Popen(["pagan", "--output=/tmp/0101010101.png", "0101"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert not stderr From 264496124fd7d00f9620c0cedd85cfaa89f3f54e Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 01:37:48 +0200 Subject: [PATCH 44/58] test on failure, fals param or no input --- test/test_bin_pagan.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/test_bin_pagan.py b/test/test_bin_pagan.py index a0f32ae..a55396c 100644 --- a/test/test_bin_pagan.py +++ b/test/test_bin_pagan.py @@ -7,4 +7,21 @@ def test_simple_call(): p = Popen(["pagan", "--output=/tmp/0101010101.png", "0101"], stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() + assert stdout assert not stderr + + +def test_simple_call_err(): + p = Popen(["pagan", "--output=/tmp/0101010101.png", "--err", "0101"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert not stdout + assert stderr + + +def test_simple_call_err_no_input(): + p = Popen(["pagan", "--output=/tmp/0101010101.png"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert not stdout + assert stderr From 09df9450ceffe3963b99b1e1c80968f130308319 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 02:19:14 +0200 Subject: [PATCH 45/58] cleanup index --- tools/webserver/webserver.py | 40 +++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index 664edf4..3844be5 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -36,17 +36,37 @@


-
Current SearchPrevious Search
-

{{slogan}} + + + + +
Current SearchPrevious Search

+

+ -

{{hist1}}

-

{{hist2}} + +

-

{{hist3}} + +

+

+{{slogan}} + +

+ +

+{{hist1}} + +

+{{hist2}} + +

+{{hist3}} +

""" @@ -103,6 +123,16 @@ def index(): hist2_hash = md5.hexdigest() md5.update(hist3) hist3_hash = md5.hexdigest() + + def add_wbr(s): + l = list(s) + for pos in range(0, len(l), 10): + l[pos] = l[pos] + " " + return s + return "".join(l) + hist1 = add_wbr(hist1) + hist2 = add_wbr(hist2) + hist3 = add_wbr(hist3) return template(TEMPLATEINDEX, slogan=slogan, hist1=hist1, hist2=hist2, hist3=hist3, sloganHash=slogan_hash, hist1Hash=hist1_hash, From 1a070c5dff910678a610fd56b6f6712a79e5873a Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 02:20:04 +0200 Subject: [PATCH 46/58] rm wbr add --- tools/webserver/webserver.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index 3844be5..941a4cf 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -124,15 +124,6 @@ def index(): md5.update(hist3) hist3_hash = md5.hexdigest() - def add_wbr(s): - l = list(s) - for pos in range(0, len(l), 10): - l[pos] = l[pos] + " " - return s - return "".join(l) - hist1 = add_wbr(hist1) - hist2 = add_wbr(hist2) - hist3 = add_wbr(hist3) return template(TEMPLATEINDEX, slogan=slogan, hist1=hist1, hist2=hist2, hist3=hist3, sloganHash=slogan_hash, hist1Hash=hist1_hash, From e17e423bb6d823f837cab87fff40ddf00ccd9519 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 13:07:38 +0200 Subject: [PATCH 47/58] default hash is md5 --- tools/console/pagan | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/console/pagan b/tools/console/pagan index 288f649..303749a 100644 --- a/tools/console/pagan +++ b/tools/console/pagan @@ -15,8 +15,7 @@ parser.add_argument("--output", help="save image to specific output path") parser.add_argument("--hash", default="MD5", help="use hash function, allowed %s, default %s" % - (generator.HASHES.values(), - list(generator.HASHES.values())[0])) + (generator.HASHES.values(), "MD5")) args = parser.parse_args() From 405b7fffa34d05ed258960dc016b2e821eb01663 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 18 Oct 2016 21:41:28 +0200 Subject: [PATCH 48/58] use bottle app, changed 404 message --- tools/webserver/webserver.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index 941a4cf..a357034 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -1,10 +1,8 @@ """pagan webserver""" +from bottle import Bottle from bottle import debug -from bottle import error -from bottle import post from bottle import request from bottle import response -from bottle import route from bottle import run from bottle import static_file from bottle import template @@ -70,8 +68,10 @@

""" +app = Bottle() -@error(404) + +@app.error(404) def error404(code): """handle error 404 """ return """
@@ -82,8 +82,8 @@ def error404(code): front door.""" -@route('/') -@post('/') +@app.route('/') +@app.post('/') def index(): """main functionality of webserver""" default = ["pagan", "python", "avatar", "github"] @@ -130,7 +130,7 @@ def index(): hist2Hash=hist2_hash, hist3Hash=hist3_hash) -@route('/himage/') +@app.route('/himage/') def hashimage(hashvalue): """generate image by hash, usese tempfile :-/""" tmpf = tempfile.mkstemp(".png")[1] @@ -143,5 +143,15 @@ def hashimage(hashvalue): image.save("/", tmpf) return static_file(tmpf, root="/") + +@app.route('/coverage_exit') +def coverage_exit(): + """exit function for coverage""" + import os + import signal + if os.environ["COVERAGE_EXIT"] != "True": + return "" + os.kill(os.getpid(), signal.SIGTERM) + debug(True) -run(host='localhost', port=8080) +run(app, host='localhost', port=8080) From 9c5fd6e98bdfeef21bc9ec459b7921d904dc064f Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Thu, 20 Oct 2016 00:32:29 +0200 Subject: [PATCH 49/58] minor changes --- tools/webserver/webserver.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py index a357034..ba45e96 100644 --- a/tools/webserver/webserver.py +++ b/tools/webserver/webserver.py @@ -153,5 +153,6 @@ def coverage_exit(): return "" os.kill(os.getpid(), signal.SIGTERM) -debug(True) -run(app, host='localhost', port=8080) +if __name__ == "__main__": + debug(True) + run(app, host='localhost', port=8080) From aab65fe283851fc73eda1a9eb6e8ab04b4e2bacb Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 8 Nov 2016 14:15:44 +0100 Subject: [PATCH 50/58] bin_pagan fixed filename, added --noshow to allow tox tests again, failed on opening image views --- test/test_bin_pagan.py | 21 ++++++++++++++++++++- tools/console/pagan | 18 +++++++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) mode change 100644 => 100755 tools/console/pagan diff --git a/test/test_bin_pagan.py b/test/test_bin_pagan.py index a55396c..3a673dd 100644 --- a/test/test_bin_pagan.py +++ b/test/test_bin_pagan.py @@ -1,10 +1,11 @@ +import os from subprocess import PIPE from subprocess import Popen def test_simple_call(): """check if calling the script simply works and yields no error""" - p = Popen(["pagan", "--output=/tmp/0101010101.png", "0101"], + p = Popen(["pagan", "--noshow", "--output=/tmp/0101010101.png", "0101"], stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() assert stdout @@ -12,6 +13,7 @@ def test_simple_call(): def test_simple_call_err(): + """fail if called with wrong arguments""" p = Popen(["pagan", "--output=/tmp/0101010101.png", "--err", "0101"], stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() @@ -20,8 +22,25 @@ def test_simple_call_err(): def test_simple_call_err_no_input(): + """fail if no input given""" p = Popen(["pagan", "--output=/tmp/0101010101.png"], stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() assert not stdout assert stderr + + +def test_replace_chars_in_filename(): + """test replace chars in filename, if only called with input + + no punctuation allowed, so replace "one.png" with "one-png" + """ + p = Popen(["pagan", "--noshow", "/tmp/one.png"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert not stderr + assert stdout + expected_filename = "-tmp-one-png.png" + assert os.path.isfile(expected_filename) + if os.path.isfile(expected_filename): + os.unlink(expected_filename) diff --git a/tools/console/pagan b/tools/console/pagan old mode 100644 new mode 100755 index b4d8309..73c9b41 --- a/tools/console/pagan +++ b/tools/console/pagan @@ -3,6 +3,7 @@ import argparse import os +import string import pagan from pagan import generator @@ -11,6 +12,8 @@ parser.add_argument("input", nargs="+", help="string, basis of avatar computing") parser.add_argument("--show", action="store_true", help="show avatar in external editor, default behavior") +parser.add_argument("--noshow", action="store_true", + help="don't show avatar in external editor") parser.add_argument("--output", help="save image to specific output path") parser.add_argument("--hash", default="MD5", @@ -19,7 +22,7 @@ parser.add_argument("--hash", default="MD5", args = parser.parse_args() -slogan = " ".join(args.input) +INPUT = " ".join(args.input) if args.hash and args.hash not in generator.HASHES.values(): HASH = "" @@ -30,15 +33,20 @@ elif args.hash and args.hash in generator.HASHES.values(): HASH = key break -if not args.output: +if not args.output and not args.noshow: args.show = True -img = pagan.Avatar(slogan, HASH) -if args.show: +img = pagan.Avatar(INPUT, HASH) +if not args.noshow and args.show: img.show() if args.output: img.save(*os.path.split(args.output)) else: + filename = args.input[0] + # make filename save again + allowed = string.ascii_letters + string.digits + "+-_" + filename = "".join([((c in allowed) and c or "-") for c in filename]) + img.save(os.getcwd(), - filename=args.input[0].replace("/", "").replace("..", "")) + filename=filename) From 41816dc0e1058aa75808ec97ec7c7a0ed5f45ed7 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 8 Nov 2016 14:16:12 +0100 Subject: [PATCH 51/58] minor pylint fixes --- pagan/pagan.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/pagan/pagan.py b/pagan/pagan.py index c561659..180fa54 100644 --- a/pagan/pagan.py +++ b/pagan/pagan.py @@ -3,7 +3,7 @@ import os -class Avatar(): +class Avatar(object): # Default output path is in the current working directory. DEFAULT_OUTPUT_PATH = os.path.join(os.getcwd(), "output/") @@ -18,8 +18,9 @@ def __init__(self, inpt, hashfun=DEFAULT_HASHFUN): self.img = self.__create_image(inpt, hashfun) def __create_image(self, inpt, hashfun): - """Creates the avatar based on the input and - the chosen hash function.""" + """Creates the avatar based on the input and the chosen hash function. + + """ if hashfun not in generator.HASHES.keys(): print ("Unknown or unsupported hash function. Using default: %s" % self.DEFAULT_HASHFUN) @@ -29,22 +30,24 @@ def __create_image(self, inpt, hashfun): return generator.generate(inpt, algo) def show(self): - """Shows a preview of the avatar in an external - image viewer.""" + """Shows a preview of the avatar in an external image viewer.""" self.img.show() def change(self, inpt, hashfun=DEFAULT_HASHFUN): """Change the avatar by providing a new input. - Uses the standard hash function if no one is given.""" + + Uses the standard hash function if no one is given. + """ self.img = self.__create_image(inpt, hashfun) def save(self, path=DEFAULT_OUTPUT_PATH, filename=DEFAULT_FILENAME): - """Saves a avatar under the given output path to - a given filename. The file ending ".png" is appended + """Saves a avatar under the given output path to a given filename. + + The file ending ".png" is appended automatically. If the path does not exist, it will be created. When no parameters are omitted, a default path - and/or filename will be used.""" - + and/or filename will be used. + """ # Creates a path when it does not exist. if not os.path.exists(path): os.makedirs(path) @@ -56,6 +59,7 @@ def save(self, path=DEFAULT_OUTPUT_PATH, filename=DEFAULT_FILENAME): # Saves the image under the given filepath. filepath = ("%s%s.png" % (path, filename)) filepath = os.path.join(path, "%s.png" % filename) + # FIXIT: filepath without SUFFIX, print writes false filename print ("Saving: %s" % filepath) self.img.save(filepath, 'PNG') From 617d806171b6562d7a5563a095df5d77ae1eb825 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 15 Nov 2016 17:29:48 +0100 Subject: [PATCH 52/58] Add test for console version of pagan, add more tests --- test/test_bin_pagan.py | 46 ++++++++++++++++++++++++++++++++++++++++++ test/test_generator.py | 2 +- test/test_pagan.py | 15 ++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 test/test_bin_pagan.py diff --git a/test/test_bin_pagan.py b/test/test_bin_pagan.py new file mode 100644 index 0000000..3a673dd --- /dev/null +++ b/test/test_bin_pagan.py @@ -0,0 +1,46 @@ +import os +from subprocess import PIPE +from subprocess import Popen + + +def test_simple_call(): + """check if calling the script simply works and yields no error""" + p = Popen(["pagan", "--noshow", "--output=/tmp/0101010101.png", "0101"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert stdout + assert not stderr + + +def test_simple_call_err(): + """fail if called with wrong arguments""" + p = Popen(["pagan", "--output=/tmp/0101010101.png", "--err", "0101"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert not stdout + assert stderr + + +def test_simple_call_err_no_input(): + """fail if no input given""" + p = Popen(["pagan", "--output=/tmp/0101010101.png"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert not stdout + assert stderr + + +def test_replace_chars_in_filename(): + """test replace chars in filename, if only called with input + + no punctuation allowed, so replace "one.png" with "one-png" + """ + p = Popen(["pagan", "--noshow", "/tmp/one.png"], + stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + assert not stderr + assert stdout + expected_filename = "-tmp-one-png.png" + assert os.path.isfile(expected_filename) + if os.path.isfile(expected_filename): + os.unlink(expected_filename) diff --git a/test/test_generator.py b/test/test_generator.py index a147b3c..b9a488e 100644 --- a/test/test_generator.py +++ b/test/test_generator.py @@ -22,4 +22,4 @@ def test_generate_by_hash(): img = pagan.generator.generate_by_hash(md5.hexdigest()[:2]) with pytest.raises(pagan.generator.FalseHashError): - img = pagan.generator.generate_by_hash(md5.hexdigest()+"A") + img = pagan.generator.generate_by_hash(md5.hexdigest()+"G") diff --git a/test/test_pagan.py b/test/test_pagan.py index ad41ce6..f2ac4b5 100644 --- a/test/test_pagan.py +++ b/test/test_pagan.py @@ -1,4 +1,7 @@ # -*- coding: latin-1 -*- +import os +import random +import string import tempfile import pagan @@ -49,6 +52,17 @@ def test_save(): img0.save(tmpdir, tmpfile) +def test_save_new_dir(): + """save file in not existing directory""" + img0 = pagan.Avatar("1") + tmpdir = tempfile.gettempdir() + testdir = ''.join(random.SystemRandom().choice(string.digits) + for _ in range(12)) + testdir = os.path.join(tmpdir, testdir) + testfile = tempfile.mkstemp("", dir=tmpdir)[1].split("/")[-1] + img0.save(testdir, testfile) + + if __name__ == "__main__": test_create() test_diffrent_hash() @@ -56,3 +70,4 @@ def test_save(): test_show() test_change() test_save() + test_save_new_dir() From ab705db8f8b34e9cfb3851f4c5268ade78e61d37 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 15 Nov 2016 17:30:36 +0100 Subject: [PATCH 53/58] Remove tools/webserver, new repo for webserver version --- tools/webserver/Readme.md | 2 - tools/webserver/webserver.py | 118 ----------------------------------- 2 files changed, 120 deletions(-) delete mode 100644 tools/webserver/Readme.md delete mode 100644 tools/webserver/webserver.py diff --git a/tools/webserver/Readme.md b/tools/webserver/Readme.md deleted file mode 100644 index 501ca58..0000000 --- a/tools/webserver/Readme.md +++ /dev/null @@ -1,2 +0,0 @@ -requirement: - bottle diff --git a/tools/webserver/webserver.py b/tools/webserver/webserver.py deleted file mode 100644 index e6bde68..0000000 --- a/tools/webserver/webserver.py +++ /dev/null @@ -1,118 +0,0 @@ -"""pagan webserver""" -from bottle import debug -from bottle import error -from bottle import post -from bottle import request -from bottle import response -from bottle import route -from bottle import run -from bottle import static_file -from bottle import template -import hashlib -import logging -import pagan -import tempfile - -logging.warning("\n%s\n\ -early work in progress, try at your own risk\n\ -this will pollute your temp dir!\n\ -%s" % ("= "*32, "= "*32)) - -TEMPLATEINDEX = """ -
-

pagan

-
-Welcome to the python avatar generator for absolute nerds. -

Generate your own Avatar. -

-

-
-
-
Current SearchPrevious Search
-

{{slogan}} -

-

- -

{{hist1}} -

-

{{hist2}} -

-

{{hist3}} -

-

-""" - - -@error(404) -def error404(code): - """handle error 404 """ - return template('{{code}} Avatar not found. You may use this:', - code=code) - - -@route('/') -@post('/') -def index(): - """main functionality of webserver""" - default = ["pagan", "python", "avatar", "github"] - slogan = request.forms.get("slogan") - - if not slogan: - if request.get_cookie("hist1"): - slogan = request.get_cookie("hist1") - else: - slogan = "pagan" - - if not request.get_cookie("hist1"): - hist1, hist2, hist3, hist4 = default[:] - else: - hist1 = request.get_cookie("hist1") - hist2 = request.get_cookie("hist2") - hist3 = request.get_cookie("hist3") - hist4 = request.get_cookie("hist4") - - if slogan in (hist1, hist2, hist3, hist4): - history = [hist1, hist2, hist3, hist4] - history.remove(slogan) - hist1, hist2, hist3 = history[0], history[1], history[2] - - response.set_cookie("hist1", slogan, max_age=60*60*24*30, httponly=True) - response.set_cookie("hist2", hist1, max_age=60*60*24*30, httponly=True) - response.set_cookie("hist3", hist2, max_age=60*60*24*30, httponly=True) - response.set_cookie("hist4", hist3, max_age=60*60*24*30, httponly=True) - # slogan, hist1, hist2, hist3 = escape(slogan), escape(hist1),\ - # escape(hist2), escape(hist3) - md5 = hashlib.md5() - md5.update(slogan) - slogan_hash = md5.hexdigest() - md5.update(hist1) - hist1_hash = md5.hexdigest() - md5.update(hist2) - hist2_hash = md5.hexdigest() - md5.update(hist3) - hist3_hash = md5.hexdigest() - return template(TEMPLATEINDEX, slogan=slogan, - hist1=hist1, hist2=hist2, hist3=hist3, - sloganHash=slogan_hash, hist1Hash=hist1_hash, - hist2Hash=hist2_hash, hist3Hash=hist3_hash) - - -@route('/himage/') -def hashimage(hashvalue): - """generate image by hash, usese tempfile :-/""" - tmpf = tempfile.mkstemp(".png")[1] - image = pagan.Avatar("") - image.img = pagan.generator.generate_by_hash(hashvalue) - image.save("/", tmpf) - return static_file(tmpf, root="/") - -debug(True) -run(host='localhost', port=8080) From 25ddc28c60d26df61637c16bea2f996e533bba2c Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 15 Nov 2016 17:31:52 +0100 Subject: [PATCH 54/58] bin/pagan sanatize file names, add --noshow option --- tools/console/pagan | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) mode change 100644 => 100755 tools/console/pagan diff --git a/tools/console/pagan b/tools/console/pagan old mode 100644 new mode 100755 index ddecaf4..73c9b41 --- a/tools/console/pagan +++ b/tools/console/pagan @@ -3,6 +3,7 @@ import argparse import os +import string import pagan from pagan import generator @@ -11,29 +12,41 @@ parser.add_argument("input", nargs="+", help="string, basis of avatar computing") parser.add_argument("--show", action="store_true", help="show avatar in external editor, default behavior") +parser.add_argument("--noshow", action="store_true", + help="don't show avatar in external editor") parser.add_argument("--output", help="save image to specific output path") parser.add_argument("--hash", default="MD5", help="use hash function, allowed %s, default %s" % - (generator.HASHES.values(), list(generator.HASHES.values())[0])) + (generator.HASHES.values(), "MD5")) args = parser.parse_args() -slogan = " ".join(args.input) +INPUT = " ".join(args.input) if args.hash and args.hash not in generator.HASHES.values(): HASH = "" elif args.hash and args.hash in generator.HASHES.values(): - HASH = list(generator.HASHES.values()).index(args.hash) + # dict.keys() must not be right ordered + for key in generator.HASHES: + if args.hash == generator.HASHES[key]: + HASH = key + break -if not args.output: +if not args.output and not args.noshow: args.show = True -img = pagan.Avatar(slogan, HASH) -if args.show: +img = pagan.Avatar(INPUT, HASH) +if not args.noshow and args.show: img.show() if args.output: img.save(*os.path.split(args.output)) else: - img.save(os.getcwd(), filename=args.input[0]) \ No newline at end of file + filename = args.input[0] + # make filename save again + allowed = string.ascii_letters + string.digits + "+-_" + filename = "".join([((c in allowed) and c or "-") for c in filename]) + + img.save(os.getcwd(), + filename=filename) From 99d23dedc7abc1c56d7ea74edee044e56575e613 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Tue, 15 Nov 2016 17:32:05 +0100 Subject: [PATCH 55/58] Docstring reformat to pass pylint --- pagan/generator.py | 22 +++++++++++++--------- pagan/pagan.py | 24 ++++++++++++++---------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/pagan/generator.py b/pagan/generator.py index 3b3adc9..690c4f3 100644 --- a/pagan/generator.py +++ b/pagan/generator.py @@ -270,8 +270,10 @@ def setup_pixelmap(hashcode): def generate(str, alg): - """Generates an PIL image avatar based on the given - input String. Acts as the main accessor to pagan.""" + """Generates an PIL image avatar based on the given input String. + + Acts as the main accessor to pagan. + """ img = Image.new(IMAGE_MODE, IMAGE_SIZE, BACKGROUND_COLOR) hashcode = hash_input(str, alg) pixelmap = setup_pixelmap(hashcode) @@ -280,18 +282,20 @@ def generate(str, alg): def generate_by_hash(hashcode): - """Generates an PIL image avatar based on the given - hash String. Acts as the main accessor to pagan.""" + """Generates an PIL image avatar based on the given hash String. + + Acts as the main accessor to pagan. + """ img = Image.new(IMAGE_MODE, IMAGE_SIZE, BACKGROUND_COLOR) if len(hashcode) < 32: print ("hashcode must have lenght >= 32, %s" % hashcode) raise FalseHashError - allowed = "0123456789abcdef" - hashcheck = [c in allowed for c in hashcode] - if False in hashcheck: - print ("hashcode has not allowed structure %s" % hashcode) - raise FalseHashError + allowed = "0123456789ABCDEFabcdef" + for c in hashcode: + if c not in allowed: + print ("hashcode has not allowed structure %s" % hashcode) + raise FalseHashError pixelmap = setup_pixelmap(hashcode) draw_image(pixelmap, img) diff --git a/pagan/pagan.py b/pagan/pagan.py index c561659..180fa54 100644 --- a/pagan/pagan.py +++ b/pagan/pagan.py @@ -3,7 +3,7 @@ import os -class Avatar(): +class Avatar(object): # Default output path is in the current working directory. DEFAULT_OUTPUT_PATH = os.path.join(os.getcwd(), "output/") @@ -18,8 +18,9 @@ def __init__(self, inpt, hashfun=DEFAULT_HASHFUN): self.img = self.__create_image(inpt, hashfun) def __create_image(self, inpt, hashfun): - """Creates the avatar based on the input and - the chosen hash function.""" + """Creates the avatar based on the input and the chosen hash function. + + """ if hashfun not in generator.HASHES.keys(): print ("Unknown or unsupported hash function. Using default: %s" % self.DEFAULT_HASHFUN) @@ -29,22 +30,24 @@ def __create_image(self, inpt, hashfun): return generator.generate(inpt, algo) def show(self): - """Shows a preview of the avatar in an external - image viewer.""" + """Shows a preview of the avatar in an external image viewer.""" self.img.show() def change(self, inpt, hashfun=DEFAULT_HASHFUN): """Change the avatar by providing a new input. - Uses the standard hash function if no one is given.""" + + Uses the standard hash function if no one is given. + """ self.img = self.__create_image(inpt, hashfun) def save(self, path=DEFAULT_OUTPUT_PATH, filename=DEFAULT_FILENAME): - """Saves a avatar under the given output path to - a given filename. The file ending ".png" is appended + """Saves a avatar under the given output path to a given filename. + + The file ending ".png" is appended automatically. If the path does not exist, it will be created. When no parameters are omitted, a default path - and/or filename will be used.""" - + and/or filename will be used. + """ # Creates a path when it does not exist. if not os.path.exists(path): os.makedirs(path) @@ -56,6 +59,7 @@ def save(self, path=DEFAULT_OUTPUT_PATH, filename=DEFAULT_FILENAME): # Saves the image under the given filepath. filepath = ("%s%s.png" % (path, filename)) filepath = os.path.join(path, "%s.png" % filename) + # FIXIT: filepath without SUFFIX, print writes false filename print ("Saving: %s" % filepath) self.img.save(filepath, 'PNG') From 6d91a186a42b889529eff6b04ef35f501188f6c2 Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Mon, 28 Nov 2016 09:48:16 +0100 Subject: [PATCH 56/58] minor changes --- tools/console/pagan | 8 -------- tox.ini | 1 + 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/tools/console/pagan b/tools/console/pagan index 746717a..73c9b41 100755 --- a/tools/console/pagan +++ b/tools/console/pagan @@ -22,11 +22,7 @@ parser.add_argument("--hash", default="MD5", args = parser.parse_args() -<<<<<<< HEAD -slogan = " ".join(args.input) -======= INPUT = " ".join(args.input) ->>>>>>> testbranch if args.hash and args.hash not in generator.HASHES.values(): HASH = "" @@ -47,9 +43,6 @@ if not args.noshow and args.show: if args.output: img.save(*os.path.split(args.output)) else: -<<<<<<< HEAD - img.save(os.getcwd(), filename=args.input[0]) -======= filename = args.input[0] # make filename save again allowed = string.ascii_letters + string.digits + "+-_" @@ -57,4 +50,3 @@ else: img.save(os.getcwd(), filename=filename) ->>>>>>> testbranch diff --git a/tox.ini b/tox.ini index cd1c311..eeb8ad4 100644 --- a/tox.ini +++ b/tox.ini @@ -11,3 +11,4 @@ skip_missing_interpreters = True commands = pytest deps = pytest + hypothesis From d67e729e18cf794f2c6b28f6ed6232d7178b3abe Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Mon, 28 Nov 2016 10:52:36 +0100 Subject: [PATCH 57/58] use future module for byte strings --- pagan/generator.py | 8 ++------ setup.py | 2 +- tox.ini | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/pagan/generator.py b/pagan/generator.py index 690c4f3..51a166c 100644 --- a/pagan/generator.py +++ b/pagan/generator.py @@ -7,6 +7,7 @@ import os import sys +from builtins import bytes class FalseHashError(Exception): """ """ @@ -103,12 +104,7 @@ def hash_input(inpt, algo=HASH_SHA256): elif (algo == HASH_SHA512): hashcode = hashlib.sha512() - if sys.version_info.major == 2: - inpt = bytes(inpt) - else: - inpt = bytes(inpt, "utf-8") - - hashcode.update(inpt) + hashcode.update(bytes(inpt, "utf-8")) hexhash = hashcode.hexdigest() return hexhash diff --git a/setup.py b/setup.py index 98cc1a0..3bed64a 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,6 @@ 'Environment :: Web Environment', 'Topic :: Software Development :: Libraries :: Python Modules' ], - install_requires=['Pillow>=2.3.0'] + install_requires=['Pillow>=2.3.0', 'future'] ) diff --git a/tox.ini b/tox.ini index eeb8ad4..3f31375 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py27, py34, py35 +envlist = py27, py34, py35, py36 skip_missing_interpreters = True [testenv] From ddb8b7af80def310a4c3ebc4dfd4d05af3700bef Mon Sep 17 00:00:00 2001 From: Frank Timmermann Date: Mon, 28 Nov 2016 10:53:12 +0100 Subject: [PATCH 58/58] add hyotehsis test --- .gitignore | 2 ++ test/test_hypo.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 test/test_hypo.py diff --git a/.gitignore b/.gitignore index b532f3d..47c2ac4 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,8 @@ var/ *.egg-info/ .installed.cfg *.egg +.hypothesis/ +test/.hypothesis/ # PyInstaller # Usually these files are written by a python script from a template diff --git a/test/test_hypo.py b/test/test_hypo.py new file mode 100644 index 0000000..70dbf75 --- /dev/null +++ b/test/test_hypo.py @@ -0,0 +1,14 @@ +# -*- coding: latin-1 -*- +import pagan +from hypothesis import given +import hypothesis.strategies as st + + +@given(st.text(), st.integers()) +def test_hypo(inpt, hashnr): + img = pagan.Avatar(inpt, hashnr) + assert (img.img) + + +if __name__ == "__main__": + test_hypo()