Slide 1

Slide 1 text

Confessions of Joe Developer Daniel Greenfeld

Slide 2

Slide 2 text

Daniel Greenfeld @pydanny Who am I? Daniel Greenfeld (@pydanny) Pythonista at Cartwheel Djangonaut at Revsys Co-lead of djangopackages.com & Packaginator (Open Comparison) Fiancé of Audrey Roy http://www.flickr.com/photos/pydanny/4442245488

Slide 3

Slide 3 text

Why am I talking?

Slide 4

Slide 4 text

I’m stupid and lazy

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

I’m stupid

Slide 7

Slide 7 text

Daniel Greenfeld @pydanny I’m stupid • Can’t figure things out • Can’t remember things • Too stupid not to ask stupid questions

Slide 8

Slide 8 text

Daniel Greenfeld @pydanny I’m stupid • If I get stuck for more than 30 minutes... • Find libraries that do it for me • Ask on Twitter for answers • Stack Overflow is also good, but watch for trolls. • IRC can be good, if you get a troll try a different channel. Can’t figure things out

Slide 9

Slide 9 text

Daniel Greenfeld @pydanny I’m stupid Can’t figure things out # This sample gleefully taken from https://gist.github.com/973705 import urllib2 gh_url = 'https://api.github.com' gh_user= 'user' gh_pass = 'pass' req = urllib2.Request(gh_url) password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm() password_manager.add_password(None, gh_url, gh_user, gh_pass) auth_manager = urllib2.HTTPBasicAuthHandler(password_manager) opener = urllib2.build_opener(auth_manager) urllib2.install_opener(opener) handler = urllib2.urlopen(req) print handler.getcode() print handler.headers.getheader('content-type') # ------ # 200 # 'application/json' “Smart” people way # This sample joyfully taken from # https://gist.github.com/973705 import requests r = requests.get('https://api.github.com', auth=('user', 'pass')) print r.status_code print r.headers['content-type'] # ------ # 200 # 'application/json' PyDanny way

Slide 10

Slide 10 text

Daniel Greenfeld @pydanny I’m stupid # This sample gleefully taken from # https://gist.github.com/973705 import urllib2 gh_url = 'https://api.github.com' gh_user= 'user' gh_pass = 'pass' req = urllib2.Request(gh_url) password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm() password_manager.add_password(None, gh_url, gh_user, gh_pass) auth_manager = urllib2.HTTPBasicAuthHandler(password_manager) opener = urllib2.build_opener(auth_manager) urllib2.install_opener(opener) handler = urllib2.urlopen(req) print handler.getcode() print handler.headers.getheader('content-type') # ------ # 200 # 'application/json' ‘Smart way’ aka hard way What is this? And this? What is an install opener? Finally we make the request!

Slide 11

Slide 11 text

Daniel Greenfeld @pydanny I’m stupid Can’t figure things out # This sample joyfully taken from # https://gist.github.com/973705 import requests r = requests.get('https://api.github.com', auth=('user', 'pass')) print r.status_code print r.headers['content-type'] # ------ # 200 # 'application/json' ‘Stupid way’ aka easy way HTTP GET Username + Password

Slide 12

Slide 12 text

Daniel Greenfeld @pydanny I’m stupid Can’t figure things out # This sample gleefully taken from https://gist.github.com/973705 import urllib2 gh_url = 'https://api.github.com' gh_user= 'user' gh_pass = 'pass' req = urllib2.Request(gh_url) password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm() password_manager.add_password(None, gh_url, gh_user, gh_pass) auth_manager = urllib2.HTTPBasicAuthHandler(password_manager) opener = urllib2.build_opener(auth_manager) urllib2.install_opener(opener) handler = urllib2.urlopen(req) print handler.getcode() print handler.headers.getheader('content-type') # ------ # 200 # 'application/json' “Smart” people way # This sample joyfully taken from # https://gist.github.com/973705 import requests r = requests.get('https://api.github.com', auth=('user', 'pass')) print r.status_code print r.headers['content-type'] # ------ # 200 # 'application/json' PyDanny way

Slide 13

Slide 13 text

Daniel Greenfeld @pydanny I’m stupid • There are no stupid questions • Don’t try and impress the people around you by not asking questions. Too stupid not to ask stupid questions

Slide 14

Slide 14 text

Daniel Greenfeld @pydanny I’m stupid Too stupid not to ask stupid questions You are at DjangoCon. If you don’t ask the question, you are wasting opportunity.

Slide 15

Slide 15 text

Daniel Greenfeld @pydanny I’m stupid Too stupid not to ask stupid questions You are at DjangoCon. A positive trait good tech leads often look for is the ability to ask questions.

Slide 16

Slide 16 text

Daniel Greenfeld @pydanny I’m stupid • Documentation makes me look good • Docstrings are awesome • Learn you some Restructured Text • Write down even the slide bullets! • https://github.com/pydanny/pydanny-event-notes Can’t remember things

Slide 17

Slide 17 text

Daniel Greenfeld @pydanny Joe Developer Resources https://github.com/pydanny/django-party-pack http://django-party-pack.rtfd.org/ Where the code examples live!

Slide 18

Slide 18 text

Daniel Greenfeld @pydanny I’m stupid http://readthedocs.org/docs/django-party-pack Sphinx makes me look good!

Slide 19

Slide 19 text

Daniel Greenfeld @pydanny Docs/Sphinx Basics • pip install sphinx • make a docs directory •sphinx-quickstart • Follow instructions • Starting over is just removing your docs

Slide 20

Slide 20 text

Daniel Greenfeld @pydanny I’m stupid Sphinx makes me look good! http://readthedocs.org/docs/django-party-pack/en/latest/_sources/install.txt ============= Installation ============= .. note:: For things with **font like this** it means type it at the command line and hit enter. The Basics =========== 0. **git clone https://[email protected]/pydanny/django-party-pack.git** 1. Make sure you have virtualenv installed. 2. change directory to the directory that contains this README.rst file. 3. **virtualenv pollaxe** and then **source pollaxe/bin/activate** 4. **pip install -r requirements.txt** 5. **mkdir pollaxe/coverage** Building the sphinx docs ========================= 1. change directory to docs 2. **make html** Running django-coverage ======================== 1. python manage.py test page header note block section headers

Slide 21

Slide 21 text

Daniel Greenfeld @pydanny I’m stupid http://readthedocs.org/docs/django-party-pack Sphinx makes me look good!

Slide 22

Slide 22 text

Daniel Greenfeld @pydanny I’m stupid Sphinx makes me look good! http://readthedocs.org/docs/django-party-pack/en/latest/reference_polls.html

Slide 23

Slide 23 text

Daniel Greenfeld @pydanny I’m stupid Sphinx makes me look good! http://readthedocs.org/docs/django-party-pack/en/latest/reference_polls.html

Slide 24

Slide 24 text

Daniel Greenfeld @pydanny I’m stupid Sphinx makes me look good! http://readthedocs.org/docs/django-party-pack/en/latest/reference_polls.html ======================== Reference for Polls App ======================== The polls app is a copy of the Django tutorial with some mild PEP-8 cleanup. ``polls.models`` ================= .. automodule:: polls.models :members: ``polls.views`` ================= .. automodule:: polls.views :members: ``polls.tests`` ================= .. automodule:: polls.tests.test_models :members: :undoc-members: .. automodule:: polls.tests.test_views :members: :undoc-members: page header auto- model auto-model for undocumented items

Slide 25

Slide 25 text

Daniel Greenfeld @pydanny I’m stupid Sphinx makes me look good! http://readthedocs.org/docs/django-party-pack/en/latest/reference_polls.html

Slide 26

Slide 26 text

Daniel Greenfeld @pydanny I’m stupid Sphinx makes me look good! http://readthedocs.org/docs/django-party-pack/en/latest/reference_polls.html

Slide 27

Slide 27 text

Daniel Greenfeld @pydanny Sphinx walk-through http://audreyr.posterous.com/how-to-create-sphinx-docs-the-python-github-r http://bit.ly/audreyr-sphinx or

Slide 28

Slide 28 text

I’m lazy

Slide 29

Slide 29 text

Daniel Greenfeld @pydanny I’m lazy • Don’t wanna do anything twice • Don’t wanna debug code when I had it working before • Don’t wanna upload zip files per document change

Slide 30

Slide 30 text

Daniel Greenfeld @pydanny I’m lazy • If I write the same code twice I stick it in a function • Then I stick the function into a util module. • Then I put it on Github so I don’t lose it. • Isn’t this kinda the whole thing behind Open Source? Don’t wanna do anything twice

Slide 31

Slide 31 text

Daniel Greenfeld @pydanny I’m Lazy • Manually testing code by watching it run is hard... • ...and boring... • ...and hence is error prone. • Meaning you have to do more work. Don’t wanna debug code when I had it working before

Slide 32

Slide 32 text

Daniel Greenfeld @pydanny I’m Lazy Are you testing enough? Stick even the ‘basic stuff’ in the docs!

Slide 33

Slide 33 text

Daniel Greenfeld @pydanny I’m Lazy Are you testing enough? Yeah, some of the configuration is off. Will be fixed soon.

Slide 34

Slide 34 text

Daniel Greenfeld @pydanny Coverage Tricks • coverage.py is great • django-coverage runs coverage.py for Django • But you only want to test your own apps

Slide 35

Slide 35 text

Daniel Greenfeld @pydanny PREREQ_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.admin', 'django-debug-toolbar’, ) PROJECT_APPS = ( 'polls', ) INSTALLED_APPS = PREREQ_APPS + PROJECT_APPS COVERAGE_MODULE_EXCLUDES = [ 'tests$', 'settings$', 'urls$', 'locale$', 'migrations', 'fixtures', 'admin$', ] COVERAGE_MODULE_EXCLUDES += PREREQ_APPS COVERAGE_REPORT_HTML_OUTPUT_DIR = "coverage" HTML_OUTPUT_DIR = os.path.join(PROJECT_ROOT, "coverage") TEST_RUNNER = 'testrunner.OurCoverageRunner' settings.py import os.path PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__)) Super userful django or third- party-apps My custom apps INSTALLED_APPS See next page! exclude this stuff Output results here

Slide 36

Slide 36 text

Daniel Greenfeld @pydanny testrunner.py # Make our own testrunner that by default only tests our own apps from django.conf import settings from django.test.simple import DjangoTestSuiteRunner from django_coverage.coverage_runner import CoverageRunner class OurTestRunner(DjangoTestSuiteRunner): def build_suite(self, test_labels, *args, **kwargs): return super(OurTestRunner,self).build_suite(test_labels or settings.PROJECT_APPS, *args, **kwargs) class OurCoverageRunner(OurTestRunner, CoverageRunner): pass This only runs the tests on apps we want tested and skips the rest!

Slide 37

Slide 37 text

Daniel Greenfeld @pydanny I’m Lazy Are you testing enough? Yeah, some of the configuration is off. Will be fixed soon.

Slide 38

Slide 38 text

Daniel Greenfeld @pydanny I’m Lazy Don’t wanna upload zip files per document change READ THE DOCS .ORG http://readthedocs.org / http://rtfd.org

Slide 39

Slide 39 text

Daniel Greenfeld @pydanny I’m stupid • Documentation makes me look good • Docstrings are awesome • Learn you some Restructured Text • Write down even the slide bullets! • https://github.com/pydanny/pydanny-event-notes Can’t remember things • http://pydanny-event-notes.rtfd.org

Slide 40

Slide 40 text

Daniel Greenfeld @pydanny I’m Lazy Host your docs on http://rtfd.org Import your project

Slide 41

Slide 41 text

Daniel Greenfeld @pydanny I’m Lazy Host your docs on http://rtfd.org

Slide 42

Slide 42 text

Daniel Greenfeld @pydanny I’m Lazy Host your docs on http://rtfd.org Admin Service Hook ReadTheDocs Active

Slide 43

Slide 43 text

Daniel Greenfeld @pydanny I’m Lazy django-party-pack-rtfd.org

Slide 44

Slide 44 text

Daniel Greenfeld @pydanny I’m Lazy pydanny-event-notes.rtfd.org

Slide 45

Slide 45 text

Daniel Greenfeld @pydanny Sphinx Trick! /django-party-pack /docs /conf.py Makes Sphinx play nicely with Django

Slide 46

Slide 46 text

Don’t be smart and lazy

Slide 47

Slide 47 text

Daniel Greenfeld @pydanny Django Worst Practice INSTALLED_APPS += [p for p in os.listdir(BASE) if os.path.isdirr(p)] MIDDLEWARE_CLASSES = [...] def callback(arg, dirname, fnames): if 'middleware.py' in fnames: m = '%s.middleware' % os.path.split(dirnaame)[-1] MIDDLEWARE_CLASSES.append(m) urlpatterns = patterns('', ...) for app in settings.INSTALLED_APPS: if not app.startswith('django'): p = url('^%s/' % app, include('%s.urls') % app) urlpatterns += patterns('', p) ‘Magical configuration code’ Ugh. http://www.slideshare.net/jacobian/the-best-and-worst-of-django Magical settings code Magical urls code THIS CODE IS BROKEN

Slide 48

Slide 48 text

Daniel Greenfeld @pydanny Fixed Django Practice PREREQ_APPS = [ # Django "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "django.contrib.messages", "django.contrib.humanize", "django.contrib.flatpages", # external "notification", # must be first "staticfiles", "uni_form", ... ] urlpatterns = patterns("", url(r"^$", homepage, name="home"), url(r"^accounts/", include("accounts.urls")), url(r"^admin/", include(admin.site.urls)), url(r"^about/", include("about.urls")), url(r"^profiles/", include("profiles.urls")), url(r"^notices/", include("notification.urls")), ... ) MIDDLEWARE_CLASSES = [ "django.middleware.common.CommonMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "reversion.middleware.RevisionMiddleware", "django.contrib.messages.middleware.MessageMiddleware", ... ] Explicit is better then Implicit This isn’t that much typing, is it?

Slide 49

Slide 49 text

Daniel Greenfeld @pydanny Fixed Django Practice PREREQ_APPS = [ # Django "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "django.contrib.messages", "django.contrib.humanize", "django.contrib.flatpages", # external "notification", # must be first "staticfiles", "uni_form", ... ] urlpatterns = patterns("", url(r"^$", homepage, name="home"), url(r"^accounts/", include("accounts.urls")), url(r"^admin/", include(admin.site.urls)), url(r"^about/", include("about.urls")), url(r"^profiles/", include("profiles.urls")), url(r"^notices/", include("notification.urls")), ... ) MIDDLEWARE_CLASSES = [ "django.middleware.common.CommonMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "reversion.middleware.RevisionMiddleware", "django.contrib.messages.middleware.MessageMiddleware", ... ] Python’s design is predicated on the proposition that code is more often read than written. http://www.slideshare.net/jacobian/the-best-and-worst-of-django/44

Slide 50

Slide 50 text

I’m stupid and lazy

Slide 51

Slide 51 text

Daniel Greenfeld @pydanny Technical Debt • Documentation • unshared knowledge • Tests • attending to TODO statements • Code too confusing to be modified easily Postponed Activities http://bit.ly/technical-debt

Slide 52

Slide 52 text

Advocacy

Slide 53

Slide 53 text

Daniel Greenfeld @pydanny Why?

Slide 54

Slide 54 text

Daniel Greenfeld @pydanny More fun community http://www.flickr.com/photos/pydanny/5851931370/

Slide 55

Slide 55 text

Daniel Greenfeld @pydanny Jane Developer Advocacy • Now is the time • Empowerment through Education • Contribute to open source • If you don’t step up then no one will

Slide 56

Slide 56 text

Daniel Greenfeld @pydanny Joe Developer Advocacy • Give your own time • Honor your promised commitments • Give kudos to the men who bring ladies! • Step back: Don’t try to run things. How to get involved with PyLadies as a man

Slide 57

Slide 57 text

Daniel Greenfeld @pydanny Acme Corporation Advocacy http://en.wikipedia.org/wiki/File:Acme_anvil.gif

Slide 58

Slide 58 text

Daniel Greenfeld @pydanny Acme Corporation Advocacy • Sponsor because it’s the right thing to do • Sponsor because you get a tax break • Sponsor because good developers notice • Mozilla • cars.com How to get involved as a company http://en.wikipedia.org/wiki/Acme_Corporation

Slide 59

Slide 59 text

Daniel Greenfeld @pydanny Joe/Jane Developer Resources https://github.com/pydanny/django-party-pack/ http://django-party-pack.rtfd.org/ party-pack http://bit.ly/audreyr-sphinx

Slide 60

Slide 60 text

Questions?