Slide 1

Slide 1 text

Aus der Werkzeugkiste eines Python/Web-Entwicklers Wolfgang Schnerring gmbh & co. kg PyCon DE 2012, Leipzig Wolfgang Schnerring Werkzeugkiste 01.11.2012 1 / 45

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Agenda 1 Sandbox 2 Editor 3 Konsole 4 Testing 5 Deployment http://www.flickr.com/photos/zakh/337938459 by zakh Wolfgang Schnerring Werkzeugkiste 01.11.2012 4 / 45

Slide 5

Slide 5 text

Sandbox http://www.sxc.hu/photo/1379396 by Safari11

Slide 6

Slide 6 text

Sandbox Paket-Skelett setup.py README.txt CHANGES.txt HACKING.txt LICENSE.txt doc/ src/ws/pycondetalk/tests/ jeweils mit passender __init__.py Wolfgang Schnerring Werkzeugkiste 01.11.2012 6 / 45

Slide 7

Slide 7 text

Sandbox Paket-Skelett $ bin/paster create -t gocept_package ws.pycondetalk <...snip...> Enter description (One-line description of the package) ['']: An example for my talk Enter keywords (Space-separated keywords/tags) ['']: Creating template gocept_package <...snip...> Wolfgang Schnerring Werkzeugkiste 01.11.2012 7 / 45

Slide 8

Slide 8 text

Sandbox Isolation http://www.flickr.com/photos/ajc1/2799103829 by ajc1 Using the system-installed Python is considered harmful. I want YOU to use virtualenv! Wolfgang Schnerring Werkzeugkiste 01.11.2012 8 / 45

Slide 9

Slide 9 text

Sandbox Isolation http://www.flickr.com/photos/ajc1/2799103829 by ajc1 Using the system-installed Python is considered harmful. I want YOU to use virtualenv! . . . but not activate! Wolfgang Schnerring Werkzeugkiste 01.11.2012 8 / 45

Slide 10

Slide 10 text

Sandbox Pakete installieren PyPI ist hervorragend! Wolfgang Schnerring Werkzeugkiste 01.11.2012 9 / 45

Slide 11

Slide 11 text

Sandbox Pakete installieren PyPI ist hervorragend – außer, wenn er down ist Leute ihre Eggs nicht hochladen, sondern selber hosten und dann down sind Leute alte Versionen löschen . . . Wolfgang Schnerring Werkzeugkiste 01.11.2012 10 / 45

Slide 12

Slide 12 text

Sandbox Pakete installieren PyPI ist hervorragend – außer, wenn er down ist Leute ihre Eggs nicht hochladen, sondern selber hosten und dann down sind Leute alte Versionen löschen . . . Hilfreiche Parameter für pip/easy_install: index, find-links socket-timeout, allow-hosts Wolfgang Schnerring Werkzeugkiste 01.11.2012 10 / 45

Slide 13

Slide 13 text

Sandbox Pakete installieren I want YOU to pin your versions! Wolfgang Schnerring Werkzeugkiste 01.11.2012 11 / 45

Slide 14

Slide 14 text

Sandbox Pakete installieren [buildout] allow−picked−versions = false versions = versions [versions] Sphinx = 1.1.2 zc.buildout pip install -r requirements.txt ist nicht ganz das gleiche Wolfgang Schnerring Werkzeugkiste 01.11.2012 12 / 45

Slide 15

Slide 15 text

Editor http://www.sxc.hu/photo/1157698 by hisks

Slide 16

Slide 16 text

Editor Navigation: Exuberant ctags --python-kinds=-i Wolfgang Schnerring Werkzeugkiste 01.11.2012 14 / 45

Slide 17

Slide 17 text

Editor Syntax check Emacs: flymake-mode, Vim: syntastic pyflakes, importchecker, pep8, pylint Wolfgang Schnerring Werkzeugkiste 01.11.2012 15 / 45

Slide 18

Slide 18 text

http://ethanschoonover.com/solarized

Slide 19

Slide 19 text

Konsole http://en.wikipedia.org/wiki/File:Vt100-adventure.jpg by D. Fischer

Slide 20

Slide 20 text

Konsole Anwendung >>> import zope, myapp >>> um = zope.component.getUtility( myapp.usermanager.interfaces.IUserManager) >>> um.get_user( ’[email protected]’).login = ’[email protected]’ >>> import transaction >>> transaction.commit() Wolfgang Schnerring Werkzeugkiste 01.11.2012 18 / 45

Slide 21

Slide 21 text

Konsole Anwendung >>> import zope, myapp >>> um = zope.component.getUtility( myapp.usermanager.interfaces.IUserManager) >>> um.get_user( ’[email protected]’).login = ’[email protected]’ >>> import transaction >>> transaction.commit() Zope: zopectl debug Pyramid: bin/pshell etc/paste.ini Django: manage.py shell Wolfgang Schnerring Werkzeugkiste 01.11.2012 18 / 45

Slide 22

Slide 22 text

Konsole Readline-History (pdb) p self.request.get(’key’).getList() ∗∗∗ AttributeError: ’Example’ object has no attribute ’getList’ Wolfgang Schnerring Werkzeugkiste 01.11.2012 19 / 45

Slide 23

Slide 23 text

Konsole Readline-History (pdb) p self.request.get(’key’).getList() ∗∗∗ AttributeError: ’Example’ object has no attribute ’getList’ (pdb) p self.request.get(’key’).get_list() Wolfgang Schnerring Werkzeugkiste 01.11.2012 19 / 45

Slide 24

Slide 24 text

Konsole Readline-History (pdb) p self.request.get(’key’).getList() ∗∗∗ AttributeError: ’Example’ object has no attribute ’getList’ (pdb) p self.request.get(’key’).get_list() pdb (siehe https://gist.github.com/3621963) rlwrap Wolfgang Schnerring Werkzeugkiste 01.11.2012 19 / 45

Slide 25

Slide 25 text

Konsole Test-Auswahl $ bin/test −t create_user $ bin/test −s myapp.subpackage Wolfgang Schnerring Werkzeugkiste 01.11.2012 20 / 45

Slide 26

Slide 26 text

Konsole Test-Auswahl $ bin/test −t create_user $ bin/test −s myapp.subpackage zope.testrunner nose py.test stdlib unittest ab 2.7 (bzw. Backport-Paket unittest2) Wolfgang Schnerring Werkzeugkiste 01.11.2012 20 / 45

Slide 27

Slide 27 text

Konsole Test-Auswahl $ t −t create_user $ t −s myapp.subpackage Wolfgang Schnerring Werkzeugkiste 01.11.2012 21 / 45

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

Testing Test-Layers class ExampleTest(unittest.TestCase): def setUp(self): def tearDown(self): def test_something(self): Wolfgang Schnerring Werkzeugkiste 01.11.2012 23 / 45

Slide 30

Slide 30 text

Testing gemeinsames setUp Wolfgang Schnerring Werkzeugkiste 01.11.2012 24 / 45

Slide 31

Slide 31 text

Testing Stacking Wolfgang Schnerring Werkzeugkiste 01.11.2012 25 / 45

Slide 32

Slide 32 text

Testing Layer class Layer(object): __bases__ = (OTHER_LAYER,) def setUp(self): def tearDown(self): def testSetUp(self): def testTearDown(self): Wolfgang Schnerring Werkzeugkiste 01.11.2012 26 / 45

Slide 33

Slide 33 text

Testing Beispiel class PersistenceTest(unittest.TestCase): layer = DB_LAYER def test_serializing_document_parameters(self): # [...] class IntegrationTest(unittest.TestCase): layer = COMBINED_LAYER def test_search_displays_suggestions_from_database(self): # [...] Wolfgang Schnerring Werkzeugkiste 01.11.2012 27 / 45

Slide 34

Slide 34 text

Testing Beispiel class DatabaseLayer(plone.testing.Layer): def setUp(self): self.connection = dbdriver.connect(os.environ[’DSN’]) def tearDown(self): self.connection.close() DB_LAYER = DatabaseLayer() HTTP_LAYER = HTTPServerLayer() COMBINED_LAYER = plone.testing.Layer( bases=(DatabaseLayer, HTTPServerLayer)) Wolfgang Schnerring Werkzeugkiste 01.11.2012 28 / 45

Slide 35

Slide 35 text

Testing JSLint var something = { ’foo’: ’bar’, }; Wolfgang Schnerring Werkzeugkiste 01.11.2012 29 / 45

Slide 36

Slide 36 text

Testing JSLint var something = { ’foo’: ’bar’, }; $ jshint foo.js Line 1: 'something' is defined but never used. Line 2: Extra comma. Wolfgang Schnerring Werkzeugkiste 01.11.2012 29 / 45

Slide 37

Slide 37 text

Testing JSLint var something = { ’foo’: ’bar’, }; $ jshint foo.js Line 1: 'something' is defined but never used. Line 2: Extra comma. $ bin/test -t jslint Failure in test test_jslint_foo.js Traceback (most recent call last): AssertionError: JSLint foo.js:2:Extra comma. Wolfgang Schnerring Werkzeugkiste 01.11.2012 29 / 45

Slide 38

Slide 38 text

Testing gocept.jslint class JSLintTestCase(gocept.jslint.TestCase): include = (’my.package.browser:js’,) options = (gocept.jslint.TestCase.options + (’browser’, ’jquery’,)) ignore = ( "Don’t make functions within a loop", ) Wolfgang Schnerring Werkzeugkiste 01.11.2012 30 / 45

Slide 39

Slide 39 text

Testing JavaScript Unit-Tests require ’my_app.js’ describe ’MyApp’, −> it ’has read Douglas Adams’, −> expect(new MyApp().calculate_the_answer()).toEqual(42) Wolfgang Schnerring Werkzeugkiste 01.11.2012 31 / 45

Slide 40

Slide 40 text

Testing JavaScript Unit-Tests require ’my_app.js’ describe ’MyApp’, −> it ’has read Douglas Adams’, −> expect(new MyApp().calculate_the_answer()).toEqual(42) def test_suite(): return gocept.exttest.makeSuite( os.environ.get(’jasmine−bin’), ’−−coffee’, pkg_resources.resource_filename(’your.package’, ’tests’)) gocept.exttest ist noch im Alpha-Stadium. Wolfgang Schnerring Werkzeugkiste 01.11.2012 31 / 45

Slide 41

Slide 41 text

Testing Selenium: Browser-Fernsteuerung Wolfgang Schnerring Werkzeugkiste 01.11.2012 32 / 45

Slide 42

Slide 42 text

Testing Selenium: Browser-Fernsteuerung Wolfgang Schnerring Werkzeugkiste 01.11.2012 33 / 45

Slide 43

Slide 43 text

Testing gocept.selenium class AutocompleteTest(gocept.selenium.wsgi.TestCase): layer = gocept.selenium.wsgi.Layer(my_wsgi_callable) def test_search_field_should_show_suggestions(self): s = self.selenium s.open(’http://%s:%s/search_form’ % (self.layer.host, self.layer.port) s.type(’name=q’, ’zope’) s.waitForVisible(’id=suggestions’)) s.assertText(’//div[@id="suggestions"])/ul/li[2]’, ’∗fisch’) Wolfgang Schnerring Werkzeugkiste 01.11.2012 34 / 45

Slide 44

Slide 44 text

Testing Continuous Integration Wolfgang Schnerring Werkzeugkiste 01.11.2012 35 / 45

Slide 45

Slide 45 text

Testing Continuous Integration http://jenkins-ci.org http://www.shiningpanda-ci.com Wolfgang Schnerring Werkzeugkiste 01.11.2012 36 / 45

Slide 46

Slide 46 text

Deployment http://www.sxc.hu/photo/1278811 by thimoholst

Slide 47

Slide 47 text

Deployment Egg-Releases Thinking about checking out trunk to production? I want YOU to use eggs! Wolfgang Schnerring Werkzeugkiste 01.11.2012 38 / 45

Slide 48

Slide 48 text

Deployment Egg-Releases 1 Versionsnummer in setup.py eintragen, Datum in CHANGES.txt eintragen 2 Tag erzeugen 3 python setup.py sdist 4 Tarball hochladen (PyPI oder eigener Server) 5 Versionsnummer in setup.py/CHANGES.txt hochzählen zest.relaser macht das alles auf Knopfdruck Wolfgang Schnerring Werkzeugkiste 01.11.2012 39 / 45

Slide 49

Slide 49 text

Deployment http://12factor.net/ Codebase Dependencies Config Backing Services Build, release, run Processes Port binding Concurrency Disposability dev/prod parity Logs Admin processes Wolfgang Schnerring Werkzeugkiste 01.11.2012 40 / 45

Slide 50

Slide 50 text

Deployment Konfigurations-Profile base.cfg email-from = [email protected] database-url = sqlite:/// dev.cfg extends = base.cfg database-url = postgresql://localhosthost/myapp-dev production.cfg extends = base.cfg database-url = postgresql://db.example.com/myapp Wolfgang Schnerring Werkzeugkiste 01.11.2012 41 / 45

Slide 51

Slide 51 text

Deployment 1-Click-Deployment 1 Versionsnummern hochziehen 2 Tag erzeugen bzw. auf production-Branch mergen 3 Auf Produktions-Server anmelden 4 Vom VCS aktualisieren 5 Deployment laufen lassen 6 Tests laufen lassen 7 Prozesse neustarten Wolfgang Schnerring Werkzeugkiste 01.11.2012 42 / 45

Slide 52

Slide 52 text

Deployment 1-Click-Deployment def pull(): remote(’hg pull −u’) def deploy(): remote(’bin/buildout’) def test(): remote(’bin/test’) def restart(): remote(’etc/init.d/myproduct−app1 restart’) def remote(command): return fabric.api.sudo( (’cd %s && ’ + command) % SERVICE_DIR, user=SERVICE_USER) Wolfgang Schnerring Werkzeugkiste 01.11.2012 43 / 45

Slide 53

Slide 53 text

Zusammenfassung Uncle Sam sagt I want YOU to use eggs! I want YOU to use virtualenv! I want YOU to pin your versions! Wolfgang Schnerring Werkzeugkiste 01.11.2012 44 / 45

Slide 54

Slide 54 text

Gibt es Fragen? http://www.flickr.com/photos/zakh/337938498 by zakh