├── .gitignore ├── 2012-03 ├── django_on_lighttpd │ ├── README.md │ ├── fcgi_demo │ │ ├── __init__.py │ │ ├── manage.py │ │ ├── settings.py │ │ ├── templates │ │ │ └── hello.html │ │ ├── urls.py │ │ └── web │ │ │ ├── __init__.py │ │ │ ├── models.py │ │ │ ├── tests.py │ │ │ ├── urls.py │ │ │ └── views.py │ ├── lighttpd.conf │ └── restart_fastcgi.sh └── python_datetime │ ├── Python datetime.ppt │ └── README.txt ├── 2012-05 └── door-prize-meetup-api.py ├── 2012-07 ├── pygame-intro │ ├── Intro To PyGame.odp │ ├── README.md │ ├── images │ │ ├── baddie.png │ │ └── player.png │ ├── part1.py │ ├── part2.py │ ├── part3.py │ └── part4.py └── readlines │ ├── fileobject.c │ ├── manylines.txt │ ├── readlines_all.py │ ├── readlines_buffersize.py │ └── readlines_chunks.py ├── 2012-10 ├── intro │ ├── README.rst │ ├── addresses.ldif │ ├── extract_addr.py │ ├── read1.py │ ├── read2.py │ └── words.py └── sorting │ ├── README.md │ └── fun_with_sorting.pdf ├── 2012-11 ├── heroku-flask │ ├── Heroku Python Flask.pdf │ └── README.md └── intro_photocopy │ ├── README.rst │ ├── locator.py │ ├── photo0.py │ ├── photo1.py │ ├── photo2.py │ └── photocopy.py ├── 2013-02 └── intro_listifying │ ├── 00_listifying.py │ ├── 01_print_email.py │ ├── 02_email_list.py │ ├── 03_to_do.py │ ├── 03b_answers_so_i_dont_forget.py │ ├── 04_listing.py │ ├── 05_please_sir_i_want_some_more.py │ ├── 52_cards.py │ ├── README.rst │ └── addresses.ldif ├── 2013-04 ├── cleaver │ ├── Cleaver.key │ ├── README │ ├── after.py │ ├── before.py │ └── report.py └── intro_taxes │ ├── 01_csv2.py │ ├── 01b_csv2.py │ ├── 02_semantically_enhanced.py │ ├── 02b_semantically_enhanced.py │ ├── 03_split_by_bracket.txt │ ├── README.rst │ ├── brackets.py │ ├── simple_brackets.csv │ ├── split_by_bracket.py │ ├── split_by_bracket_recursive.py │ ├── tax.py │ └── tax_table.py ├── 2013-05 ├── IPython │ ├── README.rst │ ├── _images │ │ ├── home.png │ │ ├── module_path.png │ │ ├── module_source.png │ │ └── prompt.png │ ├── _static │ │ ├── ajax-loader.gif │ │ ├── basic.css │ │ ├── comment-bright.png │ │ ├── comment-close.png │ │ ├── comment.png │ │ ├── common.js │ │ ├── console.css │ │ ├── console.js │ │ ├── controller.js │ │ ├── custom.css │ │ ├── doctools.js │ │ ├── down-pressed.png │ │ ├── down.png │ │ ├── file.png │ │ ├── fonts │ │ │ ├── droidsansmono-webfont.eot │ │ │ ├── droidsansmono-webfont.svg │ │ │ ├── droidsansmono-webfont.ttf │ │ │ ├── droidsansmono-webfont.woff │ │ │ ├── generator_config.txt │ │ │ ├── opensans-italic-webfont.eot │ │ │ ├── opensans-italic-webfont.svg │ │ │ ├── opensans-italic-webfont.ttf │ │ │ ├── opensans-italic-webfont.woff │ │ │ ├── opensans-light-webfont.eot │ │ │ ├── opensans-light-webfont.svg │ │ │ ├── opensans-light-webfont.ttf │ │ │ ├── opensans-light-webfont.woff │ │ │ ├── opensans-lightitalic-webfont.eot │ │ │ ├── opensans-lightitalic-webfont.svg │ │ │ ├── opensans-lightitalic-webfont.ttf │ │ │ ├── opensans-lightitalic-webfont.woff │ │ │ ├── opensans-regular-webfont.eot │ │ │ ├── opensans-regular-webfont.svg │ │ │ ├── opensans-regular-webfont.ttf │ │ │ ├── opensans-regular-webfont.woff │ │ │ ├── opensans-semibold-webfont.eot │ │ │ ├── opensans-semibold-webfont.svg │ │ │ ├── opensans-semibold-webfont.ttf │ │ │ ├── opensans-semibold-webfont.woff │ │ │ ├── opensans-semibolditalic-webfont.eot │ │ │ ├── opensans-semibolditalic-webfont.svg │ │ │ ├── opensans-semibolditalic-webfont.ttf │ │ │ ├── opensans-semibolditalic-webfont.woff │ │ │ └── stylesheet.css │ │ ├── home.png │ │ ├── init.js │ │ ├── jquery.js │ │ ├── league.css │ │ ├── leaguegothic │ │ │ ├── LeagueGothic-CondensedItalic-webfont.eot │ │ │ ├── LeagueGothic-CondensedItalic-webfont.svg │ │ │ ├── LeagueGothic-CondensedItalic-webfont.ttf │ │ │ ├── LeagueGothic-CondensedItalic-webfont.woff │ │ │ ├── LeagueGothic-CondensedRegular-webfont.eot │ │ │ ├── LeagueGothic-CondensedRegular-webfont.svg │ │ │ ├── LeagueGothic-CondensedRegular-webfont.ttf │ │ │ ├── LeagueGothic-CondensedRegular-webfont.woff │ │ │ ├── LeagueGothic-Italic-webfont.eot │ │ │ ├── LeagueGothic-Italic-webfont.svg │ │ │ ├── LeagueGothic-Italic-webfont.ttf │ │ │ ├── LeagueGothic-Italic-webfont.woff │ │ │ ├── LeagueGothic-Regular-webfont.eot │ │ │ ├── LeagueGothic-Regular-webfont.svg │ │ │ ├── LeagueGothic-Regular-webfont.ttf │ │ │ ├── LeagueGothic-Regular-webfont.woff │ │ │ ├── SIL OFL Font License League Gothic.txt │ │ │ └── stylesheet.css │ │ ├── minus.png │ │ ├── module_path.png │ │ ├── module_source.png │ │ ├── plus.png │ │ ├── prompt.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── slides.css │ │ ├── slides.js │ │ ├── styles.css │ │ ├── sync.js │ │ ├── theme.css │ │ ├── underscore.js │ │ ├── up-pressed.png │ │ ├── up.png │ │ └── websupport.js │ └── index.html └── threading │ ├── README.md │ ├── barrier.py │ ├── callback.py │ └── producer_consumer.py ├── 2013-06 └── fizzbuzz │ ├── 01_fizz_or_buzz.py │ ├── 02_fizz_and_buzz.py │ ├── 03_number.py │ ├── boring.py │ ├── clever.py │ ├── fizzbuzz.py │ ├── fizzbuzz_tweet.py │ └── one-liner.sh ├── 2013-07 └── virtualenv │ ├── Makefile │ ├── requirements.txt │ └── source │ ├── _static │ ├── .placeholder │ └── custom.css │ ├── _templates │ └── .placeholder │ ├── conf.py │ ├── index.rst │ └── virtualenv_output.txt ├── 2013-09 ├── operator-overloading │ ├── Makefile │ └── source │ │ ├── _static │ │ └── custom.css │ │ ├── conf.py │ │ ├── index.rst │ │ ├── sortable.py │ │ └── unsorted.py └── zmq │ ├── README.rst │ ├── fizzbuzz │ ├── buzzer.py │ ├── fb.py │ ├── fizzer.py │ └── ventilator.py │ ├── hello │ ├── hwclient.py │ └── hwserver.py │ └── image_farm │ ├── cli_enqueue_images.py │ ├── farmer.py │ ├── resize_image.py │ └── sink.py ├── 2013-10 ├── flake8 │ ├── Makefile │ ├── README.rst │ └── source │ │ ├── _static │ │ └── custom.css │ │ ├── clean.py │ │ ├── commits-per-month.png │ │ ├── conf.py │ │ ├── contributors-per-month.png │ │ ├── index.rst │ │ ├── lines-of-code.png │ │ └── messy.py └── objects │ ├── 15-puzzle_old_version.py │ ├── NetworkX_sandbox.ipynb │ ├── README.rst │ ├── fifteen_puzzle │ └── __init__.py │ ├── setup.cfg │ ├── setup.py │ └── tests │ ├── __init__.py │ ├── conftest.py │ ├── test_when_calculating_valid_moves.py │ ├── test_when_create_new_puzzle.py │ └── test_when_moving.py ├── 2014-08 └── micropython │ ├── README.md │ └── demo │ ├── __init__.py │ ├── accelerometer.py │ ├── hello.py │ ├── leds.py │ ├── loop.py │ └── switch.py ├── 2014-09 ├── roman │ ├── 500BC.rst │ └── README.rst └── stdlib-tour │ ├── Makefile │ ├── requirements.txt │ └── source │ ├── _static │ └── custom.css │ ├── conf.py │ ├── dis_simple.py │ ├── index.rst │ ├── string_template.py │ ├── time_info.py │ ├── trace_main.py │ └── trace_recurse.py ├── 2015-03 └── redacted-tweets │ ├── README.md │ └── redacted_twitter_nick_loadholtes.pdf ├── 2015-06 └── effective_python │ ├── README.md │ └── effective_python_presentation.pdf ├── 2015-09 └── help-on-irc │ ├── README.md │ └── help-on-irc.pdf ├── 2017-02 └── tcp-socket-file-sharing │ ├── client │ ├── helpme.jpg │ └── serve.py ├── 2017-03-09 └── python-packaging │ ├── README.md │ ├── python_packaging.ipynb │ └── python_packaging.slides.html ├── 2017-10-12 └── python-profiling │ ├── 1.filesize.py │ ├── 2.filesize.py │ ├── 3.display_file.py │ ├── 4.pstats.py │ ├── 5.snakeviz.py │ ├── 6.line_profiling.py │ └── profiler.py ├── 2019-05-09 ├── python_in_security │ ├── CandidPythonAtlanta20190509-Final.pptx │ └── README.md └── responder-tour │ ├── LICENSE │ ├── Pipfile │ ├── Pipfile.lock │ ├── README.md │ ├── client.py │ ├── features.py │ ├── greeting.py │ └── templates │ └── template.html ├── README-hieroglyph.rst └── bin └── dates.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *~ 3 | build 4 | .venv 5 | -------------------------------------------------------------------------------- /2012-03/django_on_lighttpd/README.md: -------------------------------------------------------------------------------- 1 | Installing Django on Lighttpd with FastCGI 2 | ========================================== 3 | Speaker: JR Rickerson 4 | 5 | This was a brief lightning talk and demo of the steps necessary to get Django 6 | running under Lighttpd and FastCGI. The process was gleaned from information 7 | on the Django documentation here: 8 | https://docs.djangoproject.com/en/1.3/howto/deployment/fastcgi/ 9 | 10 | An example `lighttpd.conf` configuration file is provided here, as is a small, 11 | simple Django "Hello World!" project to test with. Additionally, a sample 12 | bash script for restarting the FastCGI processes is included. Please note that 13 | you will probably have to change several file paths to match your own install. 14 | 15 | Step 1: Installing Lighttpd 16 | --------------------------- 17 | 18 | Use your distro's package manager. For instance, on Ubuntu: 19 | apt-get install lighttpd 20 | 21 | Lighttpd comes with the fastcgi module, so it doesn't need to be installed 22 | separately. 23 | 24 | 25 | Step 2: Install Django and Flup 26 | ------------------------------- 27 | 28 | pip install django 29 | pip install flup 30 | 31 | 32 | Step 3: Configure Lighttpd 33 | -------------------------- 34 | 35 | - Enable `mod_rewrite` and `mod_fastcgi` 36 | - Set up a virtual host configuration for the right domain 37 | - Point fastcgi module to the appropriate socket file or host/port 38 | - Rewrite any urls you need to for static files 39 | - Restart lighttpd after any configuration changes: 40 | `/etc/init.d/lighttpd restart` on Ubuntu 41 | 42 | 43 | Step 4: Start the Django FastCGI process 44 | ---------------------------------------- 45 | 46 | From within the django project, use: 47 | python manage.py runfcgi 48 | and provide whatever options to match your lighttpd configuration and 49 | needs. See here for available options: 50 | https://docs.djangoproject.com/en/1.3/ref/django-admin/#django-admin-runfcgi 51 | 52 | Note that any time you upload new python changes to the webserver, you will 53 | need to restart the FastCGI processes. 54 | -------------------------------------------------------------------------------- /2012-03/django_on_lighttpd/fcgi_demo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2012-03/django_on_lighttpd/fcgi_demo/__init__.py -------------------------------------------------------------------------------- /2012-03/django_on_lighttpd/fcgi_demo/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from django.core.management import execute_manager 3 | import imp 4 | try: 5 | imp.find_module('settings') # Assumed to be in the same directory. 6 | except ImportError: 7 | import sys 8 | sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__) 9 | sys.exit(1) 10 | 11 | import settings 12 | 13 | if __name__ == "__main__": 14 | execute_manager(settings) 15 | -------------------------------------------------------------------------------- /2012-03/django_on_lighttpd/fcgi_demo/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for fcgi_demo project. 2 | 3 | DEBUG = True 4 | TEMPLATE_DEBUG = DEBUG 5 | 6 | ADMINS = ( 7 | # ('Your Name', 'your_email@example.com'), 8 | ) 9 | 10 | MANAGERS = ADMINS 11 | 12 | DATABASES = { 13 | 'default': { 14 | 'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. 15 | 'NAME': '', # Or path to database file if using sqlite3. 16 | 'USER': '', # Not used with sqlite3. 17 | 'PASSWORD': '', # Not used with sqlite3. 18 | 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. 19 | 'PORT': '', # Set to empty string for default. Not used with sqlite3. 20 | } 21 | } 22 | 23 | # Local time zone for this installation. Choices can be found here: 24 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 25 | # although not all choices may be available on all operating systems. 26 | # On Unix systems, a value of None will cause Django to use the same 27 | # timezone as the operating system. 28 | # If running in a Windows environment this must be set to the same as your 29 | # system time zone. 30 | TIME_ZONE = 'America/Chicago' 31 | 32 | # Language code for this installation. All choices can be found here: 33 | # http://www.i18nguy.com/unicode/language-identifiers.html 34 | LANGUAGE_CODE = 'en-us' 35 | 36 | SITE_ID = 1 37 | 38 | # If you set this to False, Django will make some optimizations so as not 39 | # to load the internationalization machinery. 40 | USE_I18N = True 41 | 42 | # If you set this to False, Django will not format dates, numbers and 43 | # calendars according to the current locale 44 | USE_L10N = True 45 | 46 | # Absolute filesystem path to the directory that will hold user-uploaded files. 47 | # Example: "/home/media/media.lawrence.com/media/" 48 | MEDIA_ROOT = '' 49 | 50 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 51 | # trailing slash. 52 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" 53 | MEDIA_URL = '' 54 | 55 | # Absolute path to the directory static files should be collected to. 56 | # Don't put anything in this directory yourself; store your static files 57 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. 58 | # Example: "/home/media/media.lawrence.com/static/" 59 | STATIC_ROOT = '' 60 | 61 | # URL prefix for static files. 62 | # Example: "http://media.lawrence.com/static/" 63 | STATIC_URL = '/static/' 64 | 65 | # URL prefix for admin static files -- CSS, JavaScript and images. 66 | # Make sure to use a trailing slash. 67 | # Examples: "http://foo.com/static/admin/", "/static/admin/". 68 | ADMIN_MEDIA_PREFIX = '/static/admin/' 69 | 70 | # Additional locations of static files 71 | STATICFILES_DIRS = ( 72 | # Put strings here, like "/home/html/static" or "C:/www/django/static". 73 | # Always use forward slashes, even on Windows. 74 | # Don't forget to use absolute paths, not relative paths. 75 | ) 76 | 77 | # List of finder classes that know how to find static files in 78 | # various locations. 79 | STATICFILES_FINDERS = ( 80 | 'django.contrib.staticfiles.finders.FileSystemFinder', 81 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 82 | # 'django.contrib.staticfiles.finders.DefaultStorageFinder', 83 | ) 84 | 85 | # Make this unique, and don't share it with anybody. 86 | SECRET_KEY = '3=k6_crfkqhm08$=%44+uuf34nt-3luy9oe*_%3vdwa6%o=-t#' 87 | 88 | # List of callables that know how to import templates from various sources. 89 | TEMPLATE_LOADERS = ( 90 | 'django.template.loaders.filesystem.Loader', 91 | 'django.template.loaders.app_directories.Loader', 92 | # 'django.template.loaders.eggs.Loader', 93 | ) 94 | 95 | MIDDLEWARE_CLASSES = ( 96 | 'django.middleware.common.CommonMiddleware', 97 | 'django.contrib.sessions.middleware.SessionMiddleware', 98 | 'django.middleware.csrf.CsrfViewMiddleware', 99 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 100 | 'django.contrib.messages.middleware.MessageMiddleware', 101 | ) 102 | 103 | ROOT_URLCONF = 'fcgi_demo.urls' 104 | 105 | TEMPLATE_DIRS = ( 106 | '/home/reddog/code/pyatl/django_lighttpd/templates', 107 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". 108 | # Always use forward slashes, even on Windows. 109 | # Don't forget to use absolute paths, not relative paths. 110 | ) 111 | 112 | INSTALLED_APPS = ( 113 | 'django.contrib.auth', 114 | 'django.contrib.contenttypes', 115 | 'django.contrib.sessions', 116 | 'django.contrib.sites', 117 | 'django.contrib.messages', 118 | 'django.contrib.staticfiles', 119 | 'web', 120 | # Uncomment the next line to enable the admin: 121 | # 'django.contrib.admin', 122 | # Uncomment the next line to enable admin documentation: 123 | # 'django.contrib.admindocs', 124 | ) 125 | 126 | FORCE_SCRIPT_NAME='' 127 | 128 | # A sample logging configuration. The only tangible logging 129 | # performed by this configuration is to send an email to 130 | # the site admins on every HTTP 500 error. 131 | # See http://docs.djangoproject.com/en/dev/topics/logging for 132 | # more details on how to customize your logging configuration. 133 | LOGGING = { 134 | 'version': 1, 135 | 'disable_existing_loggers': False, 136 | 'handlers': { 137 | 'mail_admins': { 138 | 'level': 'ERROR', 139 | 'class': 'django.utils.log.AdminEmailHandler' 140 | } 141 | }, 142 | 'loggers': { 143 | 'django.request': { 144 | 'handlers': ['mail_admins'], 145 | 'level': 'ERROR', 146 | 'propagate': True, 147 | }, 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /2012-03/django_on_lighttpd/fcgi_demo/templates/hello.html: -------------------------------------------------------------------------------- 1 | 2 | Hello World 3 | 4 |

{{ greeting }}, World!

5 | 6 | 7 | -------------------------------------------------------------------------------- /2012-03/django_on_lighttpd/fcgi_demo/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls.defaults import patterns, include, url 2 | from django.views.generic.simple import redirect_to 3 | 4 | # Uncomment the next two lines to enable the admin: 5 | # from django.contrib import admin 6 | # admin.autodiscover() 7 | 8 | urlpatterns = patterns('', 9 | url(r'^$', redirect_to, { 'url':'/web' }), 10 | url(r'^web/', include('fcgi_demo.web.urls')), 11 | # Examples: 12 | # url(r'^$', 'fcgi_demo.views.home', name='home'), 13 | # url(r'^fcgi_demo/', include('fcgi_demo.foo.urls')), 14 | 15 | # Uncomment the admin/doc line below to enable admin documentation: 16 | # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), 17 | 18 | # Uncomment the next line to enable the admin: 19 | # url(r'^admin/', include(admin.site.urls)), 20 | ) 21 | -------------------------------------------------------------------------------- /2012-03/django_on_lighttpd/fcgi_demo/web/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2012-03/django_on_lighttpd/fcgi_demo/web/__init__.py -------------------------------------------------------------------------------- /2012-03/django_on_lighttpd/fcgi_demo/web/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /2012-03/django_on_lighttpd/fcgi_demo/web/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /2012-03/django_on_lighttpd/fcgi_demo/web/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls.defaults import patterns, url 2 | 3 | urlpatterns = patterns('fcgi_demo.web.views', 4 | url(r'^$', 'hello', name='hello'), 5 | ) 6 | 7 | -------------------------------------------------------------------------------- /2012-03/django_on_lighttpd/fcgi_demo/web/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | from django.shortcuts import render_to_response 3 | from django.template import RequestContext 4 | 5 | def hello(request): 6 | return render_to_response('hello.html', 7 | context_instance=RequestContext(request, { 8 | 'greeting': 'Guten Abend' 9 | })) 10 | 11 | -------------------------------------------------------------------------------- /2012-03/django_on_lighttpd/lighttpd.conf: -------------------------------------------------------------------------------- 1 | # Debian lighttpd configuration file 2 | # 3 | 4 | ############ Options you really have to take care of #################### 5 | 6 | ## modules to load 7 | # mod_access, mod_accesslog and mod_alias are loaded by default 8 | # all other module should only be loaded if neccesary 9 | # - saves some time 10 | # - saves memory 11 | 12 | server.modules = ( 13 | "mod_access", 14 | "mod_alias", 15 | "mod_rewrite", 16 | "mod_fastcgi", 17 | "mod_accesslog", 18 | "mod_compress", 19 | # "mod_rewrite", 20 | # "mod_redirect", 21 | # "mod_evhost", 22 | # "mod_usertrack", 23 | # "mod_rrdtool", 24 | # "mod_webdav", 25 | # "mod_expire", 26 | # "mod_flv_streaming", 27 | # "mod_evasive" 28 | ) 29 | 30 | ## a static document-root, for virtual-hosting take look at the 31 | ## server.virtual-* options 32 | server.document-root = "/var/www/" 33 | 34 | ## where to upload files to, purged daily. 35 | server.upload-dirs = ( "/var/cache/lighttpd/uploads" ) 36 | 37 | ## where to send error-messages to 38 | server.errorlog = "/var/log/lighttpd/error.log" 39 | 40 | ## files to check for if .../ is requested 41 | index-file.names = ( "index.php", "index.html", 42 | "index.htm", "default.htm", 43 | "index.lighttpd.html" ) 44 | 45 | 46 | ## Use the "Content-Type" extended attribute to obtain mime type if possible 47 | # mimetype.use-xattr = "enable" 48 | 49 | #### accesslog module 50 | accesslog.filename = "/var/log/lighttpd/access.log" 51 | 52 | ## deny access the file-extensions 53 | # 54 | # ~ is for backupfiles from vi, emacs, joe, ... 55 | # .inc is often used for code includes which should in general not be part 56 | # of the document-root 57 | url.access-deny = ( "~", ".inc" ) 58 | 59 | ## 60 | # which extensions should not be handle via static-file transfer 61 | # 62 | # .php, .pl, .fcgi are most often handled by mod_fastcgi or mod_cgi 63 | static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) 64 | 65 | 66 | ######### Options that are good to be but not neccesary to be changed ####### 67 | 68 | ## Use ipv6 only if available. (disabled for while, check #560837) 69 | #include_shell "/usr/share/lighttpd/use-ipv6.pl" 70 | 71 | ## bind to port (default: 80) 72 | server.port = 8888 73 | 74 | ## bind to localhost only (default: all interfaces) 75 | ## server.bind = "localhost" 76 | 77 | ## error-handler for status 404 78 | #server.error-handler-404 = "/error-handler.html" 79 | #server.error-handler-404 = "/error-handler.php" 80 | 81 | ## to help the rc.scripts 82 | server.pid-file = "/var/run/lighttpd.pid" 83 | 84 | ## 85 | ## Format: .html 86 | ## -> ..../status-404.html for 'File not found' 87 | #server.errorfile-prefix = "/var/www/" 88 | 89 | ## virtual directory listings 90 | dir-listing.encoding = "utf-8" 91 | server.dir-listing = "enable" 92 | 93 | ## send unhandled HTTP-header headers to error-log 94 | #debug.dump-unknown-headers = "enable" 95 | 96 | ### only root can use these options 97 | # 98 | # chroot() to directory (default: no chroot() ) 99 | #server.chroot = "/" 100 | 101 | ## change uid to (default: don't care) 102 | server.username = "www-data" 103 | 104 | ## change uid to (default: don't care) 105 | server.groupname = "www-data" 106 | 107 | #### compress module 108 | compress.cache-dir = "/var/cache/lighttpd/compress/" 109 | compress.filetype = ("text/plain", "text/html", "application/x-javascript", "text/css") 110 | 111 | 112 | #### url handling modules (rewrite, redirect, access) 113 | # url.rewrite = ( "^/$" => "/server-status" ) 114 | # url.redirect = ( "^/wishlist/(.+)" => "http://www.123.org/$1" ) 115 | 116 | # 117 | # define a pattern for the host url finding 118 | # %% => % sign 119 | # %0 => domain name + tld 120 | # %1 => tld 121 | # %2 => domain name without tld 122 | # %3 => subdomain 1 name 123 | # %4 => subdomain 2 name 124 | # 125 | # evhost.path-pattern = "/home/storage/dev/www/%3/htdocs/" 126 | 127 | #### expire module 128 | # expire.url = ( "/buggy/" => "access 2 hours", "/asdhas/" => "access plus 1 seconds 2 minutes") 129 | 130 | #### rrdtool 131 | # rrdtool.binary = "/usr/bin/rrdtool" 132 | # rrdtool.db-name = "/var/www/lighttpd.rrd" 133 | 134 | #### variable usage: 135 | ## variable name without "." is auto prefixed by "var." and becomes "var.bar" 136 | #bar = 1 137 | #var.mystring = "foo" 138 | 139 | ## integer add 140 | #bar += 1 141 | ## string concat, with integer cast as string, result: "www.foo1.com" 142 | #server.name = "www." + mystring + var.bar + ".com" 143 | ## array merge 144 | #index-file.names = (foo + ".php") + index-file.names 145 | #index-file.names += (foo + ".php") 146 | 147 | 148 | #### external configuration files 149 | ## mimetype mapping 150 | include_shell "/usr/share/lighttpd/create-mime.assign.pl" 151 | 152 | ## load enabled configuration files, 153 | ## read /etc/lighttpd/conf-available/README first 154 | include_shell "/usr/share/lighttpd/include-conf-enabled.pl" 155 | 156 | # local fcgi demo server 157 | $HTTP["host"] == "localhost" { 158 | 159 | server.document-root = "/home/reddog/code/pyatl/django_lighttpd/fcgi_demo" 160 | 161 | fastcgi.server = ( 162 | "/fcgi_demo" => ( 163 | "main" => ( 164 | "socket" => "/home/reddog/code/pyatl/django_lighttpd/fcgi.sock", 165 | "check-local" => "disable", 166 | ) 167 | ), 168 | ) 169 | 170 | alias.url = ( 171 | "/media" => "/home/reddog/code/pyatl/django_lighttpd/fcgi_demo/media", 172 | ) 173 | 174 | url.rewrite-once = ( 175 | "^(/media.*)$" => "$1", 176 | "^/favicon.ico$" => "static/images/favicon.ico", 177 | "^/robots.txt$" => "/static/robots.txt", 178 | "^(/.*)$" => "/fcgi_demo/$1", 179 | ) 180 | } 181 | 182 | #### handle Debian Policy Manual, Section 11.5. urls 183 | ## by default allow them only from localhost 184 | ## (This must come last due to #445459) 185 | ## Note: =~ "127.0.0.1" works with ipv6 enabled, whereas == "127.0.0.1" doesn't 186 | $HTTP["remoteip"] =~ "127.0.0.1" { 187 | alias.url += ( 188 | "/doc/" => "/usr/share/doc/", 189 | "/images/" => "/usr/share/images/" 190 | ) 191 | $HTTP["url"] =~ "^/doc/|^/images/" { 192 | dir-listing.activate = "enable" 193 | } 194 | } 195 | 196 | 197 | -------------------------------------------------------------------------------- /2012-03/django_on_lighttpd/restart_fastcgi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTNAME="restart_fastcgi.sh" 4 | 5 | # Setup variables for the project 6 | PROJECTDIR="/home/reddog/code/pyatl/django_lighttpd" 7 | PROJECTPID="$PROJECTDIR/fcgi.pid" 8 | PROJECTSOCK="$PROJECTDIR/fcgi.sock" 9 | 10 | if [ -f $PROJECTPID ]; then 11 | /bin/kill `/bin/cat $PROJECTPID` 12 | /bin/rm -f $PROJECTPID 13 | fi 14 | 15 | $PROJECTDIR/manage.py runfcgi method=threaded socket=$PROJECTSOCK pidfile=$PROJECTPID maxchildren=3 16 | 17 | # This assumes the user lighttpd is running under and the user the FastCGI 18 | # process is running under are in the same group 19 | /bin/chmod 770 $PROJECTSOCK 20 | -------------------------------------------------------------------------------- /2012-03/python_datetime/Python datetime.ppt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2012-03/python_datetime/Python datetime.ppt -------------------------------------------------------------------------------- /2012-03/python_datetime/README.txt: -------------------------------------------------------------------------------- 1 | Python datetime library, adding things up 2 | Speaker: William Soukup 3 | 4 | Lightning Talk 5 | -------------------------------------------------------------------------------- /2012-05/door-prize-meetup-api.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import time 4 | import urllib 5 | import urllib2 6 | import calendar 7 | import random 8 | from datetime import datetime, timedelta 9 | 10 | API_KEY = open('%s/etc/meetup.key' % os.environ['HOME']).read().strip() 11 | base = 'https://api.meetup.com/' 12 | 13 | def main(): 14 | m = Meetup(API_KEY) 15 | # Find the event 16 | # after=datetime_to_timestamp(datetime(2012, 5, 10)) 17 | # before=datetime_to_timestamp(datetime(2012, 5, 11)) 18 | after=datetime_to_timestamp(datetime.now().date()) 19 | before=datetime_to_timestamp(datetime.now().date() + timedelta(hours=24)) 20 | events = list( 21 | m.events_for_group( 22 | 'python-atlanta', 23 | status='upcoming,past', 24 | time='%s000,%s000' % (after, before))) 25 | assert len(events) == 1 26 | event = events[0] 27 | print '%s at %s on %s' % ( 28 | event['name'], 29 | event['venue']['name'], 30 | time.ctime(event['time']/1000)) 31 | raw_input('Press enter to continue...') 32 | rsvps = m.member_rsvps(event['id']) 33 | in_drawing = [ member for member, response in rsvps if response == 'yes' ] 34 | for member in in_drawing: 35 | print member['name'] 36 | while True: 37 | raw_input('And the winner is...') 38 | winner = random.choice(in_drawing) 39 | print winner['name'] 40 | 41 | 42 | class Meetup(object): 43 | 44 | def __init__(self, api_key): 45 | self.api_key = api_key 46 | 47 | def __call__(self, endpoint, **kwargs): 48 | params = dict(kwargs) 49 | params.setdefault('key', self.api_key) 50 | # params.setdefault('page', 1) 51 | q = urllib.urlencode(params) 52 | url = base + endpoint + '.json/?' + q 53 | try: 54 | fp = urllib2.urlopen(url) 55 | text = fp.read() 56 | data = json.loads(text) 57 | except urllib2.HTTPError, e: 58 | print 'There was an error', e 59 | import pdb; pdb.set_trace() 60 | return data 61 | 62 | def member_rsvps(self, event_id): 63 | r = self('2/rsvps', event_id=event_id) 64 | for x in r['results']: 65 | yield x['member'], x['response'] 66 | 67 | def events_for_group(self, group, **kwargs): 68 | r = self('2/events', group_urlname=group, **kwargs) 69 | for x in r['results']: 70 | yield x 71 | 72 | 73 | def datetime_to_timestamp(dt): 74 | return calendar.timegm(dt.timetuple()) 75 | 76 | if __name__ == '__main__': 77 | main() 78 | -------------------------------------------------------------------------------- /2012-07/pygame-intro/Intro To PyGame.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2012-07/pygame-intro/Intro To PyGame.odp -------------------------------------------------------------------------------- /2012-07/pygame-intro/README.md: -------------------------------------------------------------------------------- 1 | "A Short Introduction to Pygame" by JR Rickerson 2 | ================================================ 3 | A quick 30 minute talk to introduce the concept of PyGame, and sample files to 4 | build a simple game during the talk. 5 | 6 | Resources in this directory 7 | --------------------------- 8 | Slideshow (OpenOffice format): Intro to PyGame.odp 9 | Images: Provided by Invent Your Own Game with Python "Dodger" tutorial. 10 | Sample code: part1.py, part2.py, part3.py, part4.py 11 | 12 | Each sample file goes along with the 4 parts of building the game described 13 | in the "Writing the Code" slides in the slideshow. 14 | 15 | Web Resources from the final slide 16 | ---------------------------------- 17 | * PyGame documentation: http://www.pygame.org 18 | * SDL documentation: http://www.libsdl.org 19 | * "Invent Your Own Games with Python" website and book: 20 | http://inventwithpython.com 21 | * Katie Cunningham's website: http://therealkatie.net 22 | -------------------------------------------------------------------------------- /2012-07/pygame-intro/images/baddie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2012-07/pygame-intro/images/baddie.png -------------------------------------------------------------------------------- /2012-07/pygame-intro/images/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2012-07/pygame-intro/images/player.png -------------------------------------------------------------------------------- /2012-07/pygame-intro/part1.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pygame 3 | 4 | from pygame.locals import QUIT 5 | 6 | # Constants 7 | WINDOWHEIGHT = 600 8 | WINDOWWIDTH = 800 9 | BACKGROUNDCOLOR = (15, 15, 15) 10 | 11 | FPS = 40 12 | 13 | def terminate(): 14 | pygame.quit() 15 | sys.exit() 16 | 17 | def main(): 18 | # Part 1: Initialize pygame, the clock, and the display surface 19 | pygame.init() 20 | mainClock = pygame.time.Clock() 21 | windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) 22 | pygame.display.set_caption('Dodge!') 23 | pygame.mouse.set_visible(False) 24 | 25 | # Part 1: Load images 26 | player_sprite = pygame.image.load('images/player.png') 27 | player_rect = player_sprite.get_rect() 28 | 29 | # Part 1: Set Player start position 30 | player_rect.topleft = (WINDOWWIDTH / 2, WINDOWHEIGHT - 50) 31 | 32 | # Start the game loop 33 | while True: 34 | for event in pygame.event.get(): 35 | if event.type == QUIT: 36 | terminate() 37 | 38 | # Render the scene 39 | windowSurface.fill(BACKGROUNDCOLOR) 40 | windowSurface.blit(player_sprite, player_rect) 41 | 42 | pygame.display.update() 43 | 44 | mainClock.tick(FPS) 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /2012-07/pygame-intro/part2.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pygame 3 | 4 | from pygame.locals import QUIT, \ 5 | KEYDOWN, KEYUP, K_UP, K_DOWN, K_LEFT, K_RIGHT, K_ESCAPE 6 | 7 | # Constants 8 | WINDOWHEIGHT = 600 9 | WINDOWWIDTH = 800 10 | BACKGROUNDCOLOR = (15, 15, 15) 11 | 12 | PLAYERSPEED = 5 13 | 14 | FPS = 40 15 | 16 | def terminate(): 17 | pygame.quit() 18 | sys.exit() 19 | 20 | def main(): 21 | # Part 1: Initialize pygame, the clock, and the display surface 22 | pygame.init() 23 | mainClock = pygame.time.Clock() 24 | windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) 25 | pygame.display.set_caption('Dodge!') 26 | pygame.mouse.set_visible(False) 27 | 28 | # Part 1: Load images 29 | player_sprite = pygame.image.load('images/player.png') 30 | player_rect = player_sprite.get_rect() 31 | 32 | # Part 1: Set Player start position 33 | player_rect.topleft = (WINDOWWIDTH / 2, WINDOWHEIGHT - 50) 34 | 35 | # Set up player movement 36 | move_left = move_right = move_up = move_down = False 37 | 38 | # Start the game loop 39 | while True: 40 | for event in pygame.event.get(): 41 | if event.type == QUIT: 42 | terminate() 43 | # Part 2: Handle keyboard input and movement 44 | elif event.type == KEYDOWN: 45 | if event.key == K_LEFT or event.key == ord('a'): 46 | move_right = False 47 | move_left = True 48 | elif event.key == K_RIGHT or event.key == ord('d'): 49 | move_left = False 50 | move_right = True 51 | elif event.key == K_UP or event.key == ord('w'): 52 | move_down = False 53 | move_up = True 54 | elif event.key == K_DOWN or event.key == ord('s'): 55 | move_up = False 56 | move_down = True 57 | elif event.type == KEYUP: 58 | if event.key == K_ESCAPE: 59 | terminate() 60 | elif event.key == K_LEFT or event.key == ord('a'): 61 | move_left = False 62 | elif event.key == K_RIGHT or event.key == ord('d'): 63 | move_right = False 64 | elif event.key == K_UP or event.key == ord('w'): 65 | move_up = False 66 | elif event.key == K_DOWN or event.key == ord('s'): 67 | move_down = False 68 | 69 | # Part 2: Handle player movememnt 70 | if move_left and player_rect.left > 0: 71 | player_rect.move_ip(-1 * PLAYERSPEED, 0) 72 | if move_right and player_rect.right < WINDOWWIDTH: 73 | player_rect.move_ip(PLAYERSPEED, 0) 74 | if move_up and player_rect.top > 0: 75 | player_rect.move_ip(0, -1 * PLAYERSPEED) 76 | if move_down and player_rect.bottom < WINDOWHEIGHT: 77 | player_rect.move_ip(0, PLAYERSPEED) 78 | 79 | # Part 1: Render the scene 80 | windowSurface.fill(BACKGROUNDCOLOR) 81 | windowSurface.blit(player_sprite, player_rect) 82 | 83 | pygame.display.update() 84 | 85 | mainClock.tick(FPS) 86 | 87 | if __name__ == '__main__': 88 | main() 89 | -------------------------------------------------------------------------------- /2012-07/pygame-intro/part3.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import random 3 | import pygame 4 | 5 | from pygame.locals import QUIT, \ 6 | KEYDOWN, KEYUP, K_UP, K_DOWN, K_LEFT, K_RIGHT, K_ESCAPE 7 | 8 | # Constants 9 | WINDOWHEIGHT = 600 10 | WINDOWWIDTH = 800 11 | BACKGROUNDCOLOR = (15, 15, 15) 12 | 13 | # Size of Baddie sprite 14 | BADDIE_WIDTH = 40 15 | BADDIE_HEIGHT = 40 16 | # Pixel move rate per loop iteration 17 | BADDIEMINSPEED = 1 18 | BADDIEMAXSPEED = 8 19 | # Number of loop iterations between adding new bombs 20 | NEWBADDIERATE = 6 21 | 22 | PLAYERSPEED = 5 23 | 24 | FPS = 40 25 | 26 | def terminate(): 27 | pygame.quit() 28 | sys.exit() 29 | 30 | def main(): 31 | # Part 1: Initialize pygame, the clock, and the display surface 32 | pygame.init() 33 | mainClock = pygame.time.Clock() 34 | windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) 35 | pygame.display.set_caption('Dodge!') 36 | pygame.mouse.set_visible(False) 37 | 38 | # Part 1: Load images 39 | player_sprite = pygame.image.load('images/player.png') 40 | player_rect = player_sprite.get_rect() 41 | baddie_sprite = pygame.image.load('images/baddie.png') 42 | 43 | # Part 1: Set Player start position 44 | player_rect.topleft = (WINDOWWIDTH / 2, WINDOWHEIGHT - 50) 45 | 46 | # Set up player movement 47 | move_left = move_right = move_up = move_down = False 48 | 49 | baddies = [] 50 | baddie_add_counter = 0 51 | 52 | # Start the game loop 53 | while True: 54 | for event in pygame.event.get(): 55 | if event.type == QUIT: 56 | terminate() 57 | # Part 2: Handle keyboard input and movement 58 | elif event.type == KEYDOWN: 59 | if event.key == K_LEFT or event.key == ord('a'): 60 | move_right = False 61 | move_left = True 62 | elif event.key == K_RIGHT or event.key == ord('d'): 63 | move_left = False 64 | move_right = True 65 | elif event.key == K_UP or event.key == ord('w'): 66 | move_down = False 67 | move_up = True 68 | elif event.key == K_DOWN or event.key == ord('s'): 69 | move_up = False 70 | move_down = True 71 | elif event.type == KEYUP: 72 | if event.key == K_ESCAPE: 73 | terminate() 74 | elif event.key == K_LEFT or event.key == ord('a'): 75 | move_left = False 76 | elif event.key == K_RIGHT or event.key == ord('d'): 77 | move_right = False 78 | elif event.key == K_UP or event.key == ord('w'): 79 | move_up = False 80 | elif event.key == K_DOWN or event.key == ord('s'): 81 | move_down = False 82 | 83 | # Part 3: Managing addition of new enemies 84 | baddie_add_counter += 1 85 | if NEWBADDIERATE == baddie_add_counter: 86 | baddie_add_counter = 0 87 | new_baddie = { 88 | 'rect': pygame.Rect( 89 | random.randint(0, WINDOWWIDTH - BADDIE_WIDTH), 90 | 0 - BADDIE_HEIGHT, 91 | BADDIE_WIDTH, 92 | BADDIE_HEIGHT 93 | ), 94 | 'speed':random.randint(BADDIEMINSPEED, BADDIEMAXSPEED), 95 | 'surface': pygame.transform.scale(baddie_sprite, 96 | (BADDIE_WIDTH, BADDIE_HEIGHT)) 97 | } 98 | 99 | baddies.append(new_baddie) 100 | 101 | # Part 2: Handle player movememnt 102 | if move_left and player_rect.left > 0: 103 | player_rect.move_ip(-1 * PLAYERSPEED, 0) 104 | if move_right and player_rect.right < WINDOWWIDTH: 105 | player_rect.move_ip(PLAYERSPEED, 0) 106 | if move_up and player_rect.top > 0: 107 | player_rect.move_ip(0, -1 * PLAYERSPEED) 108 | if move_down and player_rect.bottom < WINDOWHEIGHT: 109 | player_rect.move_ip(0, PLAYERSPEED) 110 | 111 | # Part 3: Move the baddies 112 | for baddie in baddies: 113 | baddie['rect'].move_ip(0, baddie['speed']) 114 | 115 | # Part 3: Remove any invisible baddies 116 | for baddie in baddies[:]: 117 | if baddie['rect'].top > WINDOWHEIGHT: 118 | baddies.remove(baddie) 119 | 120 | # Part 1: Render the scene 121 | windowSurface.fill(BACKGROUNDCOLOR) 122 | windowSurface.blit(player_sprite, player_rect) 123 | 124 | # Part 3: Add Baddies 125 | for baddie in baddies: 126 | windowSurface.blit(baddie['surface'], baddie['rect']) 127 | 128 | pygame.display.update() 129 | 130 | mainClock.tick(FPS) 131 | 132 | if __name__ == '__main__': 133 | main() 134 | -------------------------------------------------------------------------------- /2012-07/pygame-intro/part4.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import random 3 | import pygame 4 | 5 | from pygame.locals import QUIT, \ 6 | KEYDOWN, KEYUP, K_UP, K_DOWN, K_LEFT, K_RIGHT, K_ESCAPE 7 | 8 | # Constants 9 | WINDOWHEIGHT = 600 10 | WINDOWWIDTH = 800 11 | BACKGROUNDCOLOR = (15, 15, 15) 12 | TEXTCOLOR = (255, 255, 255) 13 | 14 | # Size of Baddie sprite 15 | BADDIE_WIDTH = 40 16 | BADDIE_HEIGHT = 40 17 | # Pixel move rate per loop iteration 18 | BADDIEMINSPEED = 1 19 | BADDIEMAXSPEED = 8 20 | # Number of loop iterations between adding new bombs 21 | NEWBADDIERATE = 6 22 | 23 | PLAYERSPEED = 5 24 | 25 | FPS = 40 26 | 27 | def terminate(): 28 | pygame.quit() 29 | sys.exit() 30 | 31 | def main(): 32 | # Part 1: Initialize pygame, the clock, and the display surface 33 | pygame.init() 34 | mainClock = pygame.time.Clock() 35 | windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) 36 | pygame.display.set_caption('Dodge!') 37 | pygame.mouse.set_visible(False) 38 | # Part 4: Fonts 39 | font = pygame.font.SysFont(None, 48) 40 | 41 | # Part 1: Load images 42 | player_sprite = pygame.image.load('images/player.png') 43 | player_rect = player_sprite.get_rect() 44 | baddie_sprite = pygame.image.load('images/baddie.png') 45 | 46 | # Part 1: Set Player start position 47 | player_rect.topleft = (WINDOWWIDTH / 2, WINDOWHEIGHT - 50) 48 | 49 | # Set up player movement 50 | move_left = move_right = move_up = move_down = False 51 | 52 | baddies = [] 53 | baddie_add_counter = 0 54 | # Part 4: Start the score at 0 55 | score = 0 56 | 57 | # Start the game loop 58 | while True: 59 | # Part 4: Increase the score the longer the player lasts 60 | score += 1 61 | 62 | for event in pygame.event.get(): 63 | if event.type == QUIT: 64 | terminate() 65 | # Part 2: Handle keyboard input and movement 66 | elif event.type == KEYDOWN: 67 | if event.key == K_LEFT or event.key == ord('a'): 68 | move_right = False 69 | move_left = True 70 | elif event.key == K_RIGHT or event.key == ord('d'): 71 | move_left = False 72 | move_right = True 73 | elif event.key == K_UP or event.key == ord('w'): 74 | move_down = False 75 | move_up = True 76 | elif event.key == K_DOWN or event.key == ord('s'): 77 | move_up = False 78 | move_down = True 79 | elif event.type == KEYUP: 80 | if event.key == K_ESCAPE: 81 | terminate() 82 | elif event.key == K_LEFT or event.key == ord('a'): 83 | move_left = False 84 | elif event.key == K_RIGHT or event.key == ord('d'): 85 | move_right = False 86 | elif event.key == K_UP or event.key == ord('w'): 87 | move_up = False 88 | elif event.key == K_DOWN or event.key == ord('s'): 89 | move_down = False 90 | 91 | # Part 3: Managing addition of new enemies 92 | baddie_add_counter += 1 93 | if NEWBADDIERATE == baddie_add_counter: 94 | baddie_add_counter = 0 95 | new_baddie = { 96 | 'rect': pygame.Rect( 97 | random.randint(0, WINDOWWIDTH - BADDIE_WIDTH), 98 | 0 - BADDIE_HEIGHT, 99 | BADDIE_WIDTH, 100 | BADDIE_HEIGHT 101 | ), 102 | 'speed':random.randint(BADDIEMINSPEED, BADDIEMAXSPEED), 103 | 'surface': pygame.transform.scale(baddie_sprite, 104 | (BADDIE_WIDTH, BADDIE_HEIGHT)) 105 | } 106 | 107 | baddies.append(new_baddie) 108 | 109 | # Part 2: Handle player movememnt 110 | if move_left and player_rect.left > 0: 111 | player_rect.move_ip(-1 * PLAYERSPEED, 0) 112 | if move_right and player_rect.right < WINDOWWIDTH: 113 | player_rect.move_ip(PLAYERSPEED, 0) 114 | if move_up and player_rect.top > 0: 115 | player_rect.move_ip(0, -1 * PLAYERSPEED) 116 | if move_down and player_rect.bottom < WINDOWHEIGHT: 117 | player_rect.move_ip(0, PLAYERSPEED) 118 | 119 | # Part 3: Move the baddies 120 | for baddie in baddies: 121 | baddie['rect'].move_ip(0, baddie['speed']) 122 | 123 | # Part 3: Remove any invisible baddies 124 | for baddie in baddies[:]: 125 | if baddie['rect'].top > WINDOWHEIGHT: 126 | baddies.remove(baddie) 127 | 128 | # Part 1: Render the scene 129 | windowSurface.fill(BACKGROUNDCOLOR) 130 | 131 | # Part 4: Show score 132 | score_text = font.render('Score: {0}'.format(score), 1, TEXTCOLOR) 133 | score_rect = score_text.get_rect() 134 | score_rect.topleft = (10, 0) 135 | windowSurface.blit(score_text, score_rect) 136 | 137 | windowSurface.blit(player_sprite, player_rect) 138 | 139 | # Part 3: Add Baddies 140 | for baddie in baddies: 141 | windowSurface.blit(baddie['surface'], baddie['rect']) 142 | 143 | pygame.display.update() 144 | 145 | # Check for collisions 146 | got_hit = False 147 | for baddie in baddies: 148 | if player_rect.colliderect(baddie['rect']): 149 | got_hit = True 150 | break 151 | if got_hit: 152 | break 153 | 154 | mainClock.tick(FPS) 155 | 156 | # Part 4: End the game 157 | gameover_text = font.render('GAME OVER!', 1, TEXTCOLOR) 158 | gameover_rect = gameover_text.get_rect() 159 | gameover_rect.topleft = ((WINDOWWIDTH / 3), (WINDOWHEIGHT / 3)) 160 | windowSurface.blit(gameover_text, gameover_rect) 161 | pygame.display.update() 162 | 163 | while True: 164 | for event in pygame.event.get(): 165 | if event.type == QUIT: 166 | terminate() 167 | elif event.type == KEYDOWN: 168 | if event.key == K_ESCAPE: 169 | terminate() 170 | 171 | if __name__ == '__main__': 172 | main() 173 | -------------------------------------------------------------------------------- /2012-07/readlines/manylines.txt: -------------------------------------------------------------------------------- 1 | first line 2 | second line 3 | third line 4 | fourth line 5 | fifth line 6 | sixth line 7 | seventh line 8 | -------------------------------------------------------------------------------- /2012-07/readlines/readlines_all.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | input_filename = '/Users/dhellmann/Dropbox/Org/dreamhost.org' 4 | 5 | body = open(input_filename, 'rt').read() 6 | total_bytes = len(body) 7 | total_lines = len(body.splitlines()) 8 | print 'total bytes:', total_bytes 9 | 10 | with open(input_filename, 'rt') as f: 11 | lines = f.readlines() 12 | print 'total lines:', len(lines) 13 | -------------------------------------------------------------------------------- /2012-07/readlines/readlines_buffersize.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import itertools 4 | 5 | input_filename = '/Users/dhellmann/Dropbox/Org/dreamhost.org' 6 | 7 | total_bytes = len(open(input_filename, 'rt').read()) 8 | 9 | print '%10s %5s %5s %s' % ('bufsize', 'mult', 'lines', 'bytes') 10 | 11 | for i in itertools.chain( 12 | xrange(1, 1000, 200), 13 | xrange(1000, 6000, 1000), 14 | [8192], 15 | xrange(10000, total_bytes + 5000, 5000), 16 | ): 17 | 18 | with open(input_filename, 'rt') as f: 19 | lines = f.readlines(i) 20 | print '%10d %5d %5d %5d' % \ 21 | (i, i / 8192, len(lines), len(''.join(lines))) 22 | -------------------------------------------------------------------------------- /2012-07/readlines/readlines_chunks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | input_filename = '/Users/dhellmann/Dropbox/Org/dreamhost.org' 4 | 5 | CHUNK = 5000 6 | 7 | with open(input_filename, 'rt') as f: 8 | num_lines = 0 9 | while True: 10 | lines = f.readlines(CHUNK) 11 | print ' ', len(lines) 12 | if not lines: 13 | break 14 | num_lines += len(lines) 15 | 16 | print 'total lines:', num_lines 17 | -------------------------------------------------------------------------------- /2012-10/intro/README.rst: -------------------------------------------------------------------------------- 1 | 2 | ========================= 3 | Intro to Python Scripting 4 | ========================= 5 | 6 | :Author: Daniel J. Rocco, Ph.D. 7 | 8 | 9 | A short introduction to Python scripting basics using the example of 10 | extracting email addresses from a weird data file. 11 | 12 | Topics covered: 13 | 14 | * how to read in a file 15 | * objects: *know stuff* and *do stuff* 16 | * packaging code into *functions*—named, reusable blobs of code 17 | * working with *variables*, which are named handles to objects 18 | 19 | 20 | Organization 21 | ============ 22 | 23 | ``read1.py`` 24 | opening the file, reading its data, calculating the number of characters/words 25 | 26 | ``read2.py`` 27 | package reading the file as a function, storing the result in a variable 28 | 29 | ``words.py`` 30 | splitting the file's data into words 31 | 32 | ``extract_addr.py`` 33 | put it all together, loop over the words in the file and print only the email addresses 34 | 35 | 36 | Resources 37 | ========= 38 | 39 | * `The Python Tutorial `_ 40 | * `Dive Into Python `_ 41 | -------------------------------------------------------------------------------- /2012-10/intro/addresses.ldif: -------------------------------------------------------------------------------- 1 | dn: mail=rockdj@cc.gatech.edu 2 | objectclass: top 3 | objectclass: person 4 | objectclass: organizationalPerson 5 | objectclass: inetOrgPerson 6 | objectclass: mozillaAbPersonObsolete 7 | mail: rockdj@cc.gatech.edu 8 | modifytimestamp: 0Z 9 | 10 | dn: mail=drocco@westga.edu 11 | objectclass: top 12 | objectclass: person 13 | objectclass: organizationalPerson 14 | objectclass: inetOrgPerson 15 | objectclass: mozillaAbPersonObsolete 16 | mail: drocco@westga.edu 17 | modifytimestamp: 0Z 18 | 19 | -------------------------------------------------------------------------------- /2012-10/intro/extract_addr.py: -------------------------------------------------------------------------------- 1 | def read_file_data(filename): 2 | with open(filename) as f: 3 | return f.read() 4 | 5 | def words_in(data): 6 | return data.split() 7 | 8 | 9 | def print_email_addrs(filename): 10 | # get the file 11 | data = read_file_data(filename) 12 | 13 | # get the words out of the file 14 | words = words_in(data) 15 | 16 | # print out the email addresses 17 | for word in words: 18 | if '@' in word and '=' not in word: 19 | print word 20 | 21 | print_email_addrs('addresses.ldif') 22 | 23 | 24 | -------------------------------------------------------------------------------- /2012-10/intro/read1.py: -------------------------------------------------------------------------------- 1 | with open('addresses.ldif') as f: 2 | print f.name 3 | data = f.read() 4 | 5 | print len(data) 6 | 7 | print len(data.split('\n')) 8 | 9 | 10 | -------------------------------------------------------------------------------- /2012-10/intro/read2.py: -------------------------------------------------------------------------------- 1 | def read_file_data(filename): 2 | with open(filename) as f: 3 | return f.read() 4 | 5 | data = read_file_data('addresses.ldif') 6 | 7 | -------------------------------------------------------------------------------- /2012-10/intro/words.py: -------------------------------------------------------------------------------- 1 | def read_file_data(filename): 2 | with open(filename) as f: 3 | return f.read() 4 | 5 | def words_in(data): 6 | return data.split() 7 | 8 | data = read_file_data('addresses.ldif') 9 | words = words_in(data) 10 | 11 | 12 | -------------------------------------------------------------------------------- /2012-10/sorting/README.md: -------------------------------------------------------------------------------- 1 | "Fun with Sorting - How Python Makes it Easy to Sort a List of Dictionaries" by Cliff Kachinske 2 | ================================================ 3 | A lightning talk on Python's sorting capabilties, including the "key" parameter of the "sorted" function. Discussion on how this can be used to merge results retrieved from different data sources. 4 | 5 | Resources in this directory 6 | --------------------------- 7 | Slideshow (PDF): fun_with_sorting.pdf 8 | -------------------------------------------------------------------------------- /2012-10/sorting/fun_with_sorting.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2012-10/sorting/fun_with_sorting.pdf -------------------------------------------------------------------------------- /2012-11/heroku-flask/Heroku Python Flask.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2012-11/heroku-flask/Heroku Python Flask.pdf -------------------------------------------------------------------------------- /2012-11/heroku-flask/README.md: -------------------------------------------------------------------------------- 1 | "Free Python Hosting Heroku Ubuntu Flask" by William Soukup 2 | ================================================ 3 | An introduction to use Heroku for hosting Python web projects, using the 4 | Flask web framework. Bill discusses the positive and negative aspects of 5 | using Heroku, and how for smaller projects, one can host applications for 6 | little to no cost. 7 | 8 | 9 | Resources in this directory 10 | --------------------------- 11 | Slideshow (PDF): Heroku Python Flask.pdf 12 | -------------------------------------------------------------------------------- /2012-11/intro_photocopy/README.rst: -------------------------------------------------------------------------------- 1 | 2 | ==================== 3 | How to Eat a Problem 4 | ==================== 5 | 6 | :Author: Daniel J. Rocco, Ph.D. 7 | 8 | 9 | A demonstration of software development through iterative refinement: breaking 10 | a problem into logical chunks, coding a bit, testing, and repeating until the 11 | problem is solved. 12 | 13 | The example involved a Python script to copy files. 14 | 15 | Topics covered: 16 | 17 | * iterative software development 18 | * Python's ``if`` conditional and ``for`` looping control structures 19 | * reading arguments from the command line 20 | * OS file services, including copying files walking directory trees 21 | 22 | 23 | Organization 24 | ============ 25 | 26 | ``photo0.py`` 27 | getting an argument from the command line 28 | 29 | ``photo1.py`` 30 | walking a directory tree 31 | 32 | ``photo2.py`` 33 | put it all together, copying files from one location to another 34 | 35 | ``photocopy.py`` 36 | the (somewhat rough) program that I actually use to do this task, which 37 | utilizes two external programs (``jhead`` and ``exiftool``), performs 38 | lossless, automatic image rotation based on the camera's rotation sensor, 39 | names photos by time stamp, and organizes photos by date. 40 | 41 | ``locator.py`` 42 | Helper module used by ``photocopy.py`` based on a recipe on ActiveState. 43 | It demonstrates some more sophisticated directory traversal techniques. 44 | 45 | Resources 46 | ========= 47 | 48 | * `The Python Tutorial `_ 49 | * |The os module|_ 50 | * `jhead `_ 51 | * `ExifTool `_ 52 | 53 | .. |The os module| replace:: The ``os`` module 54 | .. _The os module: http://docs.python.org/2/library/os.html -------------------------------------------------------------------------------- /2012-11/intro_photocopy/locator.py: -------------------------------------------------------------------------------- 1 | import os 2 | import fnmatch 3 | 4 | # ==================================================================== 5 | # file locator 6 | 7 | ### FIXME: need to move to a local site package 8 | def locate(pattern, root=os.curdir, absolute=True, 9 | excludeDirs=["CVS", ".svn", ".hg"]): 10 | """Locate all filenames matching supplied pattern in and below root. 11 | 12 | Given a UNIX-style filename pattern, locate all filenames in and below 13 | the supplied root directory that match the pattern. If ``root`` is 14 | omitted, ``os.curdir`` is used. 15 | 16 | Returns the absolute path of each matching filename. Change 17 | ``absolute`` to ``False`` to produce pathnames relative to ``root`` 18 | 19 | Directories contained in ``excludeDirs`` are not searched; by default 20 | ``locate()`` excludes CVS and Subversion metadata directories from its 21 | search. 22 | 23 | Since this is a generator function, the results are lazy evaluated. 24 | 25 | From: http://code.activestate.com/recipes/499305/ 26 | 27 | ---------- 28 | 29 | 30 | To iterate over every matching file and do something to it, you would 31 | normally use a for loop, like so: 32 | 33 | >>> for filename in locate("query*", absolute=False): 34 | ... print filename 35 | ... 36 | .\query_tags.py 37 | ... 38 | 39 | 40 | Sometimes, however, it is helpful to work with the entire list of 41 | matches at once: 42 | 43 | >>> filenames = [name for name in locate("*.txt", absolute=False)] 44 | >>> len(filenames) == 30 45 | True 46 | >>> r".\\data\\photos\\2009_01_06\\tags.txt" in filenames 47 | True 48 | >>> r".\\query_tags.py" in filenames 49 | False 50 | 51 | 52 | Let's make sure ``locate()`` still works when fed bogus data: 53 | 54 | >>> [name for name in locate("nonexistent.*")] 55 | [] 56 | >>> [name for name in locate("*.txt", root=".\\nonexistent")] 57 | [] 58 | """ 59 | if absolute: 60 | root = os.path.abspath(root) 61 | 62 | for path, dirs, files in os.walk(root): 63 | 64 | # remove excluded directories, helpful for skipping CVS/subversion 65 | # metadata files 66 | for directory in excludeDirs: 67 | if directory in dirs: 68 | dirs.remove(directory) 69 | 70 | for filename in fnmatch.filter(files, pattern): 71 | yield os.path.join(path, filename) 72 | 73 | -------------------------------------------------------------------------------- /2012-11/intro_photocopy/photo0.py: -------------------------------------------------------------------------------- 1 | # tell python we need system services 2 | import sys 3 | 4 | # sys.argv[1] is the first argument to our script, e.g. 'e:\': 5 | # 6 | # % python photo0.py e:\ 7 | # 8 | # what do you think this program will do if you forget it? 9 | # 10 | # % python photo0.py 11 | # 12 | source = sys.argv[1] 13 | 14 | print 'pretending to look for jpgs in', source 15 | 16 | -------------------------------------------------------------------------------- /2012-11/intro_photocopy/photo1.py: -------------------------------------------------------------------------------- 1 | # tell python we need OS and system services 2 | import os 3 | import sys 4 | 5 | 6 | # get the location of the JPEGs to copy from the command line 7 | source = sys.argv[1] 8 | 9 | print 'looking for jpgs in', source 10 | 11 | 12 | # http://docs.python.org/2/library/os.html#os.walk 13 | 14 | # starting in source, looking each subdirectory for JPEGs 15 | # 16 | # path: what directory we are in on the filesystem 17 | # files: the list of all the files in that directory 18 | for path, subdirs, files in os.walk(source): 19 | 20 | # look at each file individually 21 | for filename in files: 22 | 23 | # if it is a JPEG 24 | if filename.endswith('jpg'): 25 | 26 | # combine the path (directory) with the file name to produce the 27 | # "absolute" location of the file 28 | fullname = os.path.join(path, filename) 29 | print fullname 30 | 31 | -------------------------------------------------------------------------------- /2012-11/intro_photocopy/photo2.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from shutil import copy 5 | 6 | 7 | source = sys.argv[1] 8 | target = '/tmp/photos/' 9 | 10 | print 'copying jpgs from', source, 'to', target 11 | 12 | 13 | # http://docs.python.org/2/library/os.html#os.walk 14 | 15 | # path: what directory we are in on the filesystem 16 | # files: the list of all the files in that directory 17 | for path, subdirs, files in os.walk(source): 18 | for filename in files: 19 | if filename.endswith('jpg'): 20 | fullname = os.path.join(path, filename) 21 | copy(fullname, target) 22 | print '.', 23 | 24 | -------------------------------------------------------------------------------- /2012-11/intro_photocopy/photocopy.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from os import remove, rmdir 3 | 4 | from shutil import copy 5 | from subprocess import call 6 | import sys 7 | from tempfile import mkdtemp 8 | 9 | from locator import locate 10 | 11 | 12 | source = sys.argv[1] 13 | tempdir = mkdtemp() 14 | 15 | print('creating temporary directory ' + tempdir) 16 | 17 | print('copying files', end='') 18 | count = 0 19 | 20 | match_pattern = '*.[jn][pe][gf]' 21 | 22 | for photo in locate(match_pattern, root=source): 23 | print('.', end='') 24 | count += 1 25 | 26 | copy(photo, tempdir) 27 | 28 | print(' done. Copied {0} files.'.format(count)) 29 | 30 | print('auto rotate and date stamp photos...') 31 | 32 | call('jhead -ft -autorot {0}/*.jpg'.format(tempdir)) 33 | 34 | print('copying photo files...') 35 | 36 | call('exiftool "-Filename`_ 64 | * |The random module|_ 65 | 66 | .. |The random module| replace:: The ``random`` module 67 | .. _The random module: http://docs.python.org/2/library/random.html -------------------------------------------------------------------------------- /2013-02/intro_listifying/addresses.ldif: -------------------------------------------------------------------------------- 1 | n: mail=ooo what does this button do? 2 | objectclass: top 3 | objectclass: person 4 | objectclass: organizationalPerson 5 | objectclass: inetOrgPerson 6 | objectclass: mozillaAbPersonObsolete 7 | mail: i_am@pyatl.org 8 | modifytimestamp: 0Z 9 | 10 | dn: mail=drocco@westga.edu 11 | objectclass: top 12 | objectclass: person 13 | objectclass: organizationalPerson 14 | objectclass: inetOrgPerson 15 | objectclass: mozillaAbPersonObsolete 16 | mail: drocco@westga.edu 17 | modifytimestamp: 0Z 18 | 19 | dn: mail=rockdj@cc.gatech.edu 20 | objectclass: top 21 | objectclass: person 22 | objectclass: organizationalPerson 23 | objectclass: inetOrgPerson 24 | objectclass: mozillaAbPersonObsolete 25 | mail: rockdj@cc.gatech.edu 26 | modifytimestamp: 0Z 27 | 28 | dn: mail=whatever 29 | objectclass: top 30 | objectclass: person 31 | objectclass: organizationalPerson 32 | objectclass: inetOrgPerson 33 | objectclass: mozillaAbPersonObsolete 34 | mail: bored@pyatl.org 35 | modifytimestamp: 0Z 36 | 37 | dn: mail=whatever 38 | objectclass: top 39 | objectclass: person 40 | objectclass: organizationalPerson 41 | objectclass: inetOrgPerson 42 | objectclass: mozillaAbPersonObsolete 43 | mail: will_he_ever@shut.up 44 | modifytimestamp: 0Z 45 | -------------------------------------------------------------------------------- /2013-04/cleaver/Cleaver.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-04/cleaver/Cleaver.key -------------------------------------------------------------------------------- /2013-04/cleaver/README: -------------------------------------------------------------------------------- 1 | To run the experiments: 2 | 3 | $ pip install flask cleaver sqlalchemy 4 | $ python after.py 5 | 6 | ...and visit http://localhost:8080 in different browsers. 7 | 8 | To view the results: 9 | $ python report.py 10 | 11 | ...and visit http://localhost:8081. 12 | -------------------------------------------------------------------------------- /2013-04/cleaver/after.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | from cleaver import SplitMiddleware 3 | from cleaver.backend.db import SQLAlchemyBackend 4 | 5 | app = Flask(__name__) 6 | app.wsgi_app = SplitMiddleware( 7 | app.wsgi_app, 8 | lambda environ: environ['HTTP_USER_AGENT'], # Track by browser 9 | SQLAlchemyBackend('sqlite:///experiment.data') 10 | ) 11 | 12 | template = """ 13 |
14 | 15 |
16 | """ 17 | 18 | @app.route("/") 19 | def home(): 20 | # Visiting / in a web browser will render a button 21 | return template % ( 22 | request.environ['cleaver']( 23 | 'call-to-action', 24 | ('control', 'Click Here, Dummy!'), 25 | ('test', 'Free Puppies!') 26 | ) 27 | ) 28 | 29 | @app.route("/register", methods=['POST']) 30 | def register(): 31 | # Submitting the form will go to /register to display a thank-you 32 | request.environ['cleaver'].score('call-to-action') 33 | return "Thanks for Signing Up!" 34 | 35 | if __name__ == '__main__': 36 | app.run(port=8080) 37 | -------------------------------------------------------------------------------- /2013-04/cleaver/before.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | app = Flask(__name__) 4 | 5 | template = """ 6 |
7 | 8 |
9 | """ 10 | 11 | @app.route("/") 12 | def home(): 13 | # Visiting / in a web browser will render a button 14 | return template 15 | 16 | @app.route("/register", methods=['POST']) 17 | def register(): 18 | return "Thanks for Signing Up!" 19 | 20 | if __name__ == '__main__': 21 | app.run(port=8080) 22 | -------------------------------------------------------------------------------- /2013-04/cleaver/report.py: -------------------------------------------------------------------------------- 1 | from cleaver.reports.web import CleaverWebUI 2 | from cleaver.backend.db import SQLAlchemyBackend 3 | 4 | app = CleaverWebUI(SQLAlchemyBackend('sqlite:///experiment.data')) 5 | 6 | from wsgiref import simple_server 7 | print "Serving at 0.0.0.0:8081" 8 | simple_server.make_server('', 8081, app).serve_forever() 9 | -------------------------------------------------------------------------------- /2013-04/intro_taxes/01_csv2.py: -------------------------------------------------------------------------------- 1 | import csv 2 | 3 | with open('simple_brackets.csv', 'rb') as f: 4 | reader = csv.reader(f) 5 | for row in reader: 6 | print ', '.join(row) 7 | -------------------------------------------------------------------------------- /2013-04/intro_taxes/01b_csv2.py: -------------------------------------------------------------------------------- 1 | import csv 2 | 3 | with open('simple_brackets.csv', 'rb') as f: 4 | reader = csv.reader(f) 5 | brackets = list(reader) 6 | 7 | print brackets 8 | -------------------------------------------------------------------------------- /2013-04/intro_taxes/02_semantically_enhanced.py: -------------------------------------------------------------------------------- 1 | import csv 2 | from collections import namedtuple 3 | 4 | # create a namedtuple to represent the rows of our csv file 5 | Bracket = namedtuple( 6 | 'Bracket', 7 | 'tax_rate single married_joint' 8 | ) 9 | 10 | with open('simple_brackets.csv', 'rb') as f: 11 | reader = csv.reader(f) 12 | 13 | # map calls a function (Bracket._make) on each element of a list (or list- 14 | # like object). Roughly translated to English, this line reads "make a 15 | # Bracket object out of each row of the csv file, storing the list of 16 | # Brackets in the variable 'brackets'" 17 | brackets = map(Bracket._make, reader) 18 | 19 | print brackets 20 | -------------------------------------------------------------------------------- /2013-04/intro_taxes/02b_semantically_enhanced.py: -------------------------------------------------------------------------------- 1 | import csv 2 | from collections import namedtuple 3 | 4 | Bracket = namedtuple( 5 | 'Bracket', 6 | 'tax_rate single married_joint' 7 | ) 8 | 9 | with open('simple_brackets.csv', 'rb') as f: 10 | reader = csv.reader(f) 11 | 12 | # skip the header 13 | next(reader) 14 | 15 | brackets = map(Bracket._make, reader) 16 | 17 | print brackets 18 | -------------------------------------------------------------------------------- /2013-04/intro_taxes/03_split_by_bracket.txt: -------------------------------------------------------------------------------- 1 | 2 | Great! Um... How do we do anything with this? 3 | --------------------------------------------- 4 | 5 | To compute tax: 6 | 7 | - Need: Brackets, Earnings, Filing Status 8 | - Steps: 9 | 10 | 1. Split earnings into brackets 11 | 2. Compute tax for each bracket 12 | 3. Sum 13 | 14 | 15 | split_by_bracket 16 | ---------------- 17 | 18 | a. start w/ lowest bracket 19 | b. if earnings < bracket 20 | -> tax is earnings * tax rate 21 | 22 | c. else tax is 23 | tax for this bracket 24 | + tax on remaining earnings 25 | 26 | -------------------------------------------------------------------------------- /2013-04/intro_taxes/README.rst: -------------------------------------------------------------------------------- 1 | 2 | ================== 3 | The Tax Man Cometh 4 | ================== 5 | 6 | --------------------------- 7 | Or: The Other Inevitability 8 | --------------------------- 9 | 10 | 11 | :Author: Daniel J. Rocco, Ph.D. 12 | 13 | 14 | Tax season gave me an excuse to look at the ``csv`` module to show how one 15 | could read in CSV data, manipulate it, and write out a CSV file using Python. 16 | 17 | Topics covered: 18 | 19 | * reading in a CSV file 20 | * creating a lightweight model of a tax bracket using ``namedtuple`` 21 | * using ``map`` to call a function on each element of a list 22 | * massaging the string data from a CSV into a more useful numeric format with 23 | Python's ``Decimal`` class 24 | * problem analysis: breaking down the "compute tax" problem into a set of 25 | prerequisites and a list of smaller steps 26 | * review of some of Python's list mechanisms including comprehensions and the 27 | ``sum`` function 28 | * writing out a CSV file 29 | 30 | 31 | Organization 32 | ============ 33 | 34 | ``01_csv2.py`` 35 | read in a CSV file and print out its rows 36 | 37 | ``01b_csv2.py`` 38 | read in a CSV file and convert its rows to a list 39 | 40 | ``02_semantically_enhanced.py`` 41 | create a lightweight model of a tax bracket using ``namedtuple``; use the 42 | ``Bracket._make`` function to convert the rows of the CSV file to Bracket 43 | objects 44 | 45 | ``brackets.py`` 46 | further process the input data by converting strings to ``Decimal`` objects 47 | more suited for mathematical calculations 48 | 49 | ``03_split_by_bracket.txt`` 50 | sketches out the design of our tax calculator, which includes the 51 | prerequisite information needed to perform the calculation and the steps 52 | involved in the calculation itself 53 | 54 | ``split_by_bracket.py`` 55 | implementation of 03 step 1, including some sample calculations 56 | 57 | ``tax.py`` 58 | implementation of the tax calculator, including some sample calculations 59 | 60 | ``tax_table.py`` 61 | uses ``tax.py`` to write out a tax table 62 | 63 | ``simple_brackets.csv`` 64 | the example brackets, which present a simplified progressive tax model. 65 | Changing the values here (e.g. using tax schedule data available on the 66 | web) will update the brackets used by the examples above 67 | 68 | 69 | Running the Examples 70 | -------------------- 71 | 72 | I've added support code to the example Python programs above to allow them to 73 | be run standalone. All you will need is a working Python installation; for 74 | example:: 75 | 76 | $ python tax.py 77 | single $5000: 500.0 78 | single $11000: 1200.0 79 | single $24000: 4200.0 80 | 81 | married $7000: 700.0 82 | married $15000: 1500.0 83 | married $19000: 2300.0 84 | married $31000: 4800.0 85 | 86 | 87 | Recursive implementation of ``split_by_bracket`` 88 | ------------------------------------------------ 89 | 90 | Due to popular demand I have added a recursive implementation of 91 | ``split_by_bracket`` in ``split_by_bracket_recursive.py`` along with some 92 | example runs. 93 | 94 | 95 | Resources 96 | ========= 97 | 98 | * `The Python Tutorial `_ 99 | * `The csv module `_ 100 | * `The collections module, home of namedtuple `_ 101 | * `Python's Decimal `_ 102 | * `Tax Bracket Article `_; source of the simple brackets used in the demo 103 | -------------------------------------------------------------------------------- /2013-04/intro_taxes/brackets.py: -------------------------------------------------------------------------------- 1 | import csv 2 | from collections import namedtuple 3 | from decimal import Decimal 4 | 5 | 6 | Bracket = namedtuple( 7 | 'Bracket', 8 | 'tax_rate single married_joint' 9 | ) 10 | 11 | 12 | def process_row(row): 13 | """Given a row of data, make a Bracket out of the row while converting each 14 | element to a Decimal 15 | 16 | >>> process_row(('0.1', '2', '3.4')) 17 | Bracket(tax_rate=Decimal('0.1'), single=Decimal('2'), married_joint=Decimal('3.4')) 18 | 19 | """ 20 | 21 | return Bracket._make(map(Decimal, row)) 22 | 23 | 24 | with open('simple_brackets.csv', 'rb') as f: 25 | reader = csv.reader(f) 26 | 27 | # skip the header 28 | next(reader) 29 | 30 | brackets = map(process_row, reader) 31 | 32 | 33 | # we will need the brackets list later; this next bit will print out the 34 | # brackets if you say 35 | # 36 | # python brackets 37 | # 38 | # from the command line but not if some other part of the program imports them 39 | 40 | if __name__ == '__main__': 41 | print brackets 42 | -------------------------------------------------------------------------------- /2013-04/intro_taxes/simple_brackets.csv: -------------------------------------------------------------------------------- 1 | tax_bracket,single,married_jointly 2 | 0.1,10000,15000 3 | 0.2,20000,30000 4 | 0.3,0,0 5 | -------------------------------------------------------------------------------- /2013-04/intro_taxes/split_by_bracket.py: -------------------------------------------------------------------------------- 1 | from brackets import brackets 2 | 3 | 4 | def split_by_bracket(earnings, filing_status): 5 | result = [] 6 | already_taxed = 0 7 | 8 | for bracket in brackets: 9 | # pull out the max amount for this 10 | # bracket and filing_status 11 | bracket_amount = getattr(bracket, filing_status) 12 | 13 | if bracket_amount and earnings > bracket_amount: 14 | result.append((bracket_amount - already_taxed, bracket.tax_rate)) 15 | already_taxed = bracket_amount 16 | else: 17 | result.append((earnings - already_taxed, bracket.tax_rate)) 18 | break 19 | 20 | return result 21 | 22 | 23 | 24 | # as in brackets.py, this bit prints some examples if you run 25 | # 26 | # python split_by_bracket 27 | # 28 | 29 | if __name__ == '__main__': 30 | for amount in (5000, 11000, 24000): 31 | print 'single ${}:'.format(amount), \ 32 | split_by_bracket(amount, 'single') 33 | 34 | print 35 | 36 | for amount in (7000, 15000, 19000, 31000): 37 | print 'married ${}:'.format(amount), \ 38 | split_by_bracket(amount, 'married_joint') 39 | -------------------------------------------------------------------------------- /2013-04/intro_taxes/split_by_bracket_recursive.py: -------------------------------------------------------------------------------- 1 | from brackets import brackets 2 | 3 | 4 | def split_by_bracket(earnings, filing_status, _i=0, _already_taxed=0): 5 | bracket = brackets[_i] 6 | bracket_amount = getattr(bracket, filing_status) 7 | 8 | if not bracket_amount or earnings <= bracket_amount: 9 | return [(earnings - _already_taxed, bracket.tax_rate)] 10 | else: 11 | return [(bracket_amount - _already_taxed, bracket.tax_rate)] + \ 12 | split_by_bracket(earnings, filing_status, 13 | _i=_i + 1, _already_taxed=bracket_amount) 14 | 15 | 16 | if __name__ == '__main__': 17 | for amount in (5000, 11000, 24000): 18 | print 'single ${}:'.format(amount), \ 19 | split_by_bracket(amount, 'single') 20 | 21 | print 22 | 23 | for amount in (7000, 15000, 19000, 31000): 24 | print 'married ${}:'.format(amount), \ 25 | split_by_bracket(amount, 'married_joint') 26 | -------------------------------------------------------------------------------- /2013-04/intro_taxes/tax.py: -------------------------------------------------------------------------------- 1 | from split_by_bracket import split_by_bracket 2 | 3 | def tax(earnings, filing_status): 4 | earnings_by_bracket = split_by_bracket(earnings, filing_status) 5 | 6 | tax_by_bracket = [amount * tax_rate 7 | for amount, tax_rate in earnings_by_bracket] 8 | 9 | return sum(tax_by_bracket) 10 | 11 | 12 | 13 | if __name__ == '__main__': 14 | for amount in (5000, 11000, 24000): 15 | print 'single ${}:'.format(amount), tax(amount, 'single') 16 | 17 | print 18 | 19 | for amount in (7000, 15000, 19000, 31000): 20 | print 'married ${}:'.format(amount), tax(amount, 'married_joint') 21 | -------------------------------------------------------------------------------- /2013-04/intro_taxes/tax_table.py: -------------------------------------------------------------------------------- 1 | import csv 2 | 3 | from brackets import Bracket 4 | from tax import tax 5 | 6 | 7 | filename = 'simple_tax_table.csv' 8 | 9 | 10 | with open(filename, 'wb') as f: 11 | writer = csv.writer(f) 12 | writer.writerow(['earnings'] + list(Bracket._fields[1:])) 13 | 14 | for amount in xrange(5000, 100001, 5000): 15 | 16 | # first column: earnings amount 17 | data = [amount] 18 | 19 | data += [tax(amount, filing_status) 20 | for filing_status in Bracket._fields[1:]] 21 | 22 | writer.writerow(data) 23 | 24 | 25 | print 'tax table written to', filename 26 | -------------------------------------------------------------------------------- /2013-05/IPython/README.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | IPython Changes Everything 3 | ========================== 4 | 5 | :Author: | Daniel J. Rocco, Ph.D. 6 | | @drocco007 7 | 8 | In this introductory talk, we explored how IPython fits into the modern 9 | Python/OS ecosystem and sampled some of its helpful tools for exploring 10 | Python objects, source, and the filesystem. 11 | 12 | **Resources** 13 | 14 | * `IPython homepage `_ 15 | * `Raymond Hettinger's post on reading source `_ 16 | * Slides created with `Hieroglyph `_, a Sphinx extension for creating HTML5 slides -------------------------------------------------------------------------------- /2013-05/IPython/_images/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_images/home.png -------------------------------------------------------------------------------- /2013-05/IPython/_images/module_path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_images/module_path.png -------------------------------------------------------------------------------- /2013-05/IPython/_images/module_source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_images/module_source.png -------------------------------------------------------------------------------- /2013-05/IPython/_images/prompt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_images/prompt.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/ajax-loader.gif -------------------------------------------------------------------------------- /2013-05/IPython/_static/comment-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/comment-bright.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/comment-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/comment-close.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/comment.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/common.js: -------------------------------------------------------------------------------- 1 | var PERMANENT_URL_PREFIX = DOCUMENTATION_OPTIONS.URL_ROOT + '_static/'; 2 | 3 | var SLIDE_CLASSES = ['far-past', 'past', 'current', 'next', 'far-next']; 4 | var SLIDES_SELECTOR = 'section.slides > article'; 5 | 6 | var PM_TOUCH_SENSITIVITY = 15; 7 | var TABLE_CLASS = 'table'; 8 | 9 | /* ---------------------------------------------------------------------- */ 10 | /* classList polyfill by Eli Grey 11 | * (http://purl.eligrey.com/github/classList.js/blob/master/classList.js) */ 12 | 13 | if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) { 14 | 15 | (function (view) { 16 | 17 | var 18 | classListProp = "classList" 19 | , protoProp = "prototype" 20 | , elemCtrProto = (view.HTMLElement || view.Element)[protoProp] 21 | , objCtr = Object 22 | strTrim = String[protoProp].trim || function () { 23 | return this.replace(/^\s+|\s+$/g, ""); 24 | } 25 | , arrIndexOf = Array[protoProp].indexOf || function (item) { 26 | for (var i = 0, len = this.length; i < len; i++) { 27 | if (i in this && this[i] === item) { 28 | return i; 29 | } 30 | } 31 | return -1; 32 | } 33 | // Vendors: please allow content code to instantiate DOMExceptions 34 | , DOMEx = function (type, message) { 35 | this.name = type; 36 | this.code = DOMException[type]; 37 | this.message = message; 38 | } 39 | , checkTokenAndGetIndex = function (classList, token) { 40 | if (token === "") { 41 | throw new DOMEx( 42 | "SYNTAX_ERR" 43 | , "An invalid or illegal string was specified" 44 | ); 45 | } 46 | if (/\s/.test(token)) { 47 | throw new DOMEx( 48 | "INVALID_CHARACTER_ERR" 49 | , "String contains an invalid character" 50 | ); 51 | } 52 | return arrIndexOf.call(classList, token); 53 | } 54 | , ClassList = function (elem) { 55 | var 56 | trimmedClasses = strTrim.call(elem.className) 57 | , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [] 58 | ; 59 | for (var i = 0, len = classes.length; i < len; i++) { 60 | this.push(classes[i]); 61 | } 62 | this._updateClassName = function () { 63 | elem.className = this.toString(); 64 | }; 65 | } 66 | , classListProto = ClassList[protoProp] = [] 67 | , classListGetter = function () { 68 | return new ClassList(this); 69 | } 70 | ; 71 | // Most DOMException implementations don't allow calling DOMException's toString() 72 | // on non-DOMExceptions. Error's toString() is sufficient here. 73 | DOMEx[protoProp] = Error[protoProp]; 74 | classListProto.item = function (i) { 75 | return this[i] || null; 76 | }; 77 | classListProto.contains = function (token) { 78 | token += ""; 79 | return checkTokenAndGetIndex(this, token) !== -1; 80 | }; 81 | classListProto.add = function (token) { 82 | token += ""; 83 | if (checkTokenAndGetIndex(this, token) === -1) { 84 | this.push(token); 85 | this._updateClassName(); 86 | } 87 | }; 88 | classListProto.remove = function (token) { 89 | token += ""; 90 | var index = checkTokenAndGetIndex(this, token); 91 | if (index !== -1) { 92 | this.splice(index, 1); 93 | this._updateClassName(); 94 | } 95 | }; 96 | classListProto.toggle = function (token) { 97 | token += ""; 98 | if (checkTokenAndGetIndex(this, token) === -1) { 99 | this.add(token); 100 | } else { 101 | this.remove(token); 102 | } 103 | }; 104 | classListProto.toString = function () { 105 | return this.join(" "); 106 | }; 107 | 108 | if (objCtr.defineProperty) { 109 | var classListPropDesc = { 110 | get: classListGetter 111 | , enumerable: true 112 | , configurable: true 113 | }; 114 | try { 115 | objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); 116 | } catch (ex) { // IE 8 doesn't support enumerable:true 117 | if (ex.number === -0x7FF5EC54) { 118 | classListPropDesc.enumerable = false; 119 | objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); 120 | } 121 | } 122 | } else if (objCtr[protoProp].__defineGetter__) { 123 | elemCtrProto.__defineGetter__(classListProp, classListGetter); 124 | } 125 | 126 | }(self)); 127 | 128 | } 129 | /* ---------------------------------------------------------------------- */ 130 | -------------------------------------------------------------------------------- /2013-05/IPython/_static/console.css: -------------------------------------------------------------------------------- 1 | #controls { 2 | text-align: center; 3 | width: 100%; 4 | margin: 10px; 5 | font-size: 1.5em; 6 | font-family: sans-serif; 7 | } 8 | 9 | .slides.table > article { 10 | display: inline-block; 11 | } 12 | 13 | article.placeholder { 14 | background: #ddd; 15 | } 16 | 17 | .slides.table > article { 18 | position: absolute; 19 | left: 50%; 20 | margin-left: -225px; 21 | } 22 | 23 | .slides.table > article.past { 24 | transform: translate(-325px); 25 | -o-transform: translate(-325px); 26 | -moz-transform: translate(-325px); 27 | -webkit-transform: translate3d(-325px, 0, 0); 28 | 29 | } 30 | 31 | .slides.table > article.next { 32 | transform: translate(475px); 33 | -o-transform: translate(475px); 34 | -moz-transform: translate(475px); 35 | -webkit-transform: translate3d(475px, 0, 0); 36 | } 37 | 38 | .slides > article.past, 39 | .slides > article.next { 40 | height: 230px; 41 | width: 300px; 42 | 43 | margin-top: 60px; 44 | } 45 | -------------------------------------------------------------------------------- /2013-05/IPython/_static/console.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | 3 | var 4 | 5 | handleKey = function(event) { 6 | switch (event.keyCode) { 7 | case 39: // right arrow 8 | case 13: // Enter 9 | case 32: // space 10 | case 34: // PgDn 11 | nextSlide(); 12 | event.preventDefault(); 13 | break; 14 | 15 | case 37: // left arrow 16 | case 8: // Backspace 17 | case 33: // PgUp 18 | prevSlide(); 19 | event.preventDefault(); 20 | break; 21 | } 22 | }, 23 | 24 | handleUpdateSlides = function(slide_index, prev_slide, cur_slide, next_slide) { 25 | document.querySelector('#cur_slide_num').innerHTML = Number(slide_index) + 1; 26 | 27 | // make sure we have a previous and next slide to show; 28 | // if not add dummy placeholders 29 | if (!prev_slide) { 30 | prev_slide = '
'; 31 | } 32 | if (!next_slide) { 33 | next_slide = '
'; 34 | } 35 | 36 | document.querySelector('#slide_container').innerHTML = prev_slide + cur_slide + next_slide; 37 | 38 | var slides = document.querySelector('section.slides > article'); 39 | for (var i=0; i < slides.length; i++) { 40 | 41 | } 42 | }, 43 | 44 | handleMessage = function(e) { 45 | switch (e.data.command) { 46 | case 'num_slides': 47 | document.querySelector('#num_slides').innerHTML = e.data.content; 48 | break; 49 | case 'cur_slide': 50 | handleUpdateSlides(e.data.content, e.data.prev_slide, e.data.slide, e.data.next_slide); 51 | break; 52 | } 53 | }, 54 | 55 | nextSlide = function(e) { 56 | if (e) { 57 | e.preventDefault(); 58 | } 59 | window.opener.postMessage({command: 'nextSlide'}, '*'); 60 | }, 61 | 62 | prevSlide = function(e) { 63 | if (e) { 64 | e.preventDefault(); 65 | } 66 | 67 | window.opener.postMessage({command: 'prevSlide'}, '*'); 68 | }, 69 | 70 | init = function(e) { 71 | window.addEventListener('message', handleMessage, false); 72 | document.addEventListener('keydown', handleKey, false); 73 | 74 | document.querySelector('#next').addEventListener('click', nextSlide); 75 | document.querySelector('#prev').addEventListener('click', prevSlide); 76 | 77 | window.opener.postMessage({command: 'register'}, '*'); 78 | 79 | }; 80 | 81 | init(); 82 | 83 | }, false); -------------------------------------------------------------------------------- /2013-05/IPython/_static/controller.js: -------------------------------------------------------------------------------- 1 | var SlideController = ( 2 | function(){ 3 | 4 | var 5 | slidedeck, 6 | 7 | onKeyDown = function (event) { 8 | 9 | switch (event.keyCode) { 10 | case 39: // right arrow 11 | case 13: // Enter 12 | case 32: // space 13 | case 34: // PgDn 14 | slidedeck.nextSlide(); 15 | event.preventDefault(); 16 | break; 17 | 18 | case 37: // left arrow 19 | case 8: // Backspace 20 | case 33: // PgUp 21 | slidedeck.prevSlide(); 22 | event.preventDefault(); 23 | break; 24 | 25 | case 40: // down arrow 26 | if (isChromeVoxActive()) { 27 | slidedeck.speakNextItem(); 28 | } else { 29 | slidedeck.nextSlide(); 30 | } 31 | event.preventDefault(); 32 | break; 33 | 34 | case 38: // up arrow 35 | if (isChromeVoxActive()) { 36 | slidedeck.speakPrevItem(); 37 | } else { 38 | slidedeck.prevSlide(); 39 | } 40 | event.preventDefault(); 41 | break; 42 | 43 | } 44 | }; 45 | 46 | init = function(slides) { 47 | slidedeck = slides; 48 | 49 | document.addEventListener('keydown', onKeyDown, false); 50 | 51 | }; 52 | 53 | return { 54 | init: init 55 | }; 56 | 57 | }()); 58 | -------------------------------------------------------------------------------- /2013-05/IPython/_static/custom.css: -------------------------------------------------------------------------------- 1 | #ipython-changes-everything { 2 | padding-left: 0; 3 | } 4 | 5 | #ipython-changes-everything h1 { 6 | padding: 20px 0 20px 60px; 7 | background-color: rgba(200, 200, 200, 0.7); 8 | font-size: 3em; 9 | line-height: 0.85; 10 | } 11 | 12 | #ipython-changes-everything th.field-name { 13 | display: none; 14 | } 15 | 16 | #ipython-changes-everything td { 17 | padding-left: 60px; 18 | } 19 | 20 | .slides > article { 21 | border: none; 22 | box-shadow: none; 23 | font: 44px/50px'LeagueGothicRegular', Arial, sans-serif; 24 | } 25 | 26 | .slide > h1,h2 { 27 | font: 60px/68px 'LeagueGothicRegular', Arial, sans-serif; 28 | } 29 | 30 | .slide > h2 { 31 | bottom: 0px; 32 | left: 0px; 33 | background-color: rgba(0,0,0, 0.1); 34 | padding: 10px 40px; 35 | } 36 | 37 | ul li::before { 38 | content: none; 39 | } 40 | 41 | .drop { 42 | position: relative; 43 | top: 0.25em; 44 | left: -0.125em; 45 | } 46 | 47 | div.sidebar { 48 | font-size: 0.7em; 49 | } -------------------------------------------------------------------------------- /2013-05/IPython/_static/down-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/down-pressed.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/down.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/file.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/droidsansmono-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/droidsansmono-webfont.eot -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/droidsansmono-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/droidsansmono-webfont.ttf -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/droidsansmono-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/droidsansmono-webfont.woff -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/generator_config.txt: -------------------------------------------------------------------------------- 1 | # Font Squirrel Font-face Generator Configuration File 2 | # Upload this file to the generator to recreate the settings 3 | # you used to create these fonts. 4 | 5 | {"mode":"optimal","formats":["ttf","woff","eotz"],"tt_instructor":"default","fix_vertical_metrics":"Y","fix_gasp":"xy","add_spaces":"Y","add_hyphens":"Y","fallback":"none","fallback_custom":"100","options_subset":"basic","subset_custom":"","subset_custom_range":"","css_stylesheet":"stylesheet.css","filename_suffix":"-webfont","emsquare":"2048","spacing_adjustment":"0"} -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-italic-webfont.eot -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-italic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-italic-webfont.ttf -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-italic-webfont.woff -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-light-webfont.eot -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-light-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-light-webfont.ttf -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-light-webfont.woff -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-lightitalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-lightitalic-webfont.eot -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-lightitalic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-lightitalic-webfont.ttf -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-lightitalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-lightitalic-webfont.woff -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-regular-webfont.eot -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-regular-webfont.ttf -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-regular-webfont.woff -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-semibold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-semibold-webfont.eot -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-semibold-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-semibold-webfont.ttf -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-semibold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-semibold-webfont.woff -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-semibolditalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-semibolditalic-webfont.eot -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-semibolditalic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-semibolditalic-webfont.ttf -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/opensans-semibolditalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/fonts/opensans-semibolditalic-webfont.woff -------------------------------------------------------------------------------- /2013-05/IPython/_static/fonts/stylesheet.css: -------------------------------------------------------------------------------- 1 | /* Generated by Font Squirrel (http://www.fontsquirrel.com) on June 8, 2012 */ 2 | 3 | 4 | 5 | @font-face { 6 | font-family: 'Open Sans Italic'; 7 | src: url('opensans-italic-webfont.eot'); 8 | src: url('opensans-italic-webfont.eot?#iefix') format('embedded-opentype'), 9 | url('opensans-italic-webfont.woff') format('woff'), 10 | url('opensans-italic-webfont.ttf') format('truetype'), 11 | url('opensans-italic-webfont.svg#OpenSansItalic') format('svg'); 12 | font-weight: normal; 13 | font-style: normal; 14 | 15 | } 16 | 17 | 18 | 19 | 20 | @font-face { 21 | font-family: 'Open Sans'; 22 | src: url('opensans-regular-webfont.eot'); 23 | src: url('opensans-regular-webfont.eot?#iefix') format('embedded-opentype'), 24 | url('opensans-regular-webfont.woff') format('woff'), 25 | url('opensans-regular-webfont.ttf') format('truetype'), 26 | url('opensans-regular-webfont.svg#OpenSansRegular') format('svg'); 27 | font-weight: normal; 28 | font-style: normal; 29 | 30 | } 31 | 32 | 33 | 34 | 35 | @font-face { 36 | font-family: 'Open Sans Semibold'; 37 | src: url('opensans-semibold-webfont.eot'); 38 | src: url('opensans-semibold-webfont.eot?#iefix') format('embedded-opentype'), 39 | url('opensans-semibold-webfont.woff') format('woff'), 40 | url('opensans-semibold-webfont.ttf') format('truetype'), 41 | url('opensans-semibold-webfont.svg#OpenSansSemiboldRegular') format('svg'); 42 | font-weight: normal; 43 | font-style: normal; 44 | 45 | } 46 | 47 | 48 | 49 | 50 | @font-face { 51 | font-family: 'Open Sans Semibold Italic'; 52 | src: url('opensans-semibolditalic-webfont.eot'); 53 | src: url('opensans-semibolditalic-webfont.eot?#iefix') format('embedded-opentype'), 54 | url('opensans-semibolditalic-webfont.woff') format('woff'), 55 | url('opensans-semibolditalic-webfont.ttf') format('truetype'), 56 | url('opensans-semibolditalic-webfont.svg#OpenSansSemiboldItalic') format('svg'); 57 | font-weight: normal; 58 | font-style: normal; 59 | 60 | } 61 | 62 | 63 | 64 | 65 | @font-face { 66 | font-family: 'Droid Sans Mono'; 67 | src: url('droidsansmono-webfont.eot'); 68 | src: url('droidsansmono-webfont.eot?#iefix') format('embedded-opentype'), 69 | url('droidsansmono-webfont.woff') format('woff'), 70 | url('droidsansmono-webfont.ttf') format('truetype'), 71 | url('droidsansmono-webfont.svg#DroidSansMonoRegular') format('svg'); 72 | font-weight: normal; 73 | font-style: normal; 74 | 75 | } 76 | 77 | 78 | 79 | 80 | @font-face { 81 | font-family: 'Open Sans Light'; 82 | src: url('opensans-light-webfont.eot'); 83 | src: url('opensans-light-webfont.eot?#iefix') format('embedded-opentype'), 84 | url('opensans-light-webfont.woff') format('woff'), 85 | url('opensans-light-webfont.ttf') format('truetype'), 86 | url('opensans-light-webfont.svg#OpenSansLightRegular') format('svg'); 87 | font-weight: normal; 88 | font-style: normal; 89 | 90 | } 91 | 92 | 93 | 94 | 95 | @font-face { 96 | font-family: 'Open Sans Light Italic'; 97 | src: url('opensans-lightitalic-webfont.eot'); 98 | src: url('opensans-lightitalic-webfont.eot?#iefix') format('embedded-opentype'), 99 | url('opensans-lightitalic-webfont.woff') format('woff'), 100 | url('opensans-lightitalic-webfont.ttf') format('truetype'), 101 | url('opensans-lightitalic-webfont.svg#OpenSansLightItalic') format('svg'); 102 | font-weight: normal; 103 | font-style: normal; 104 | 105 | } -------------------------------------------------------------------------------- /2013-05/IPython/_static/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/home.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/init.js: -------------------------------------------------------------------------------- 1 | SlideSync.init(SlideDeck); 2 | SlideController.init(SlideSync); 3 | -------------------------------------------------------------------------------- /2013-05/IPython/_static/league.css: -------------------------------------------------------------------------------- 1 | @import url(slides.css); 2 | @import url(leaguegothic/stylesheet.css); 3 | 4 | ::-webkit-scrollbar { 5 | display: none; 6 | } 7 | 8 | body { 9 | background: white; 10 | } 11 | 12 | .slides > article { 13 | font: 60px/68px 'LeagueGothicRegular', Arial, sans-serif; 14 | letter-spacing: 0; 15 | -webkit-font-smoothing: antialiased; 16 | } 17 | 18 | h1, h2 { 19 | font-weight: normal; 20 | } -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/LeagueGothic-CondensedItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/leaguegothic/LeagueGothic-CondensedItalic-webfont.eot -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/LeagueGothic-CondensedItalic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/leaguegothic/LeagueGothic-CondensedItalic-webfont.ttf -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/LeagueGothic-CondensedItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/leaguegothic/LeagueGothic-CondensedItalic-webfont.woff -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/LeagueGothic-CondensedRegular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/leaguegothic/LeagueGothic-CondensedRegular-webfont.eot -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/LeagueGothic-CondensedRegular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/leaguegothic/LeagueGothic-CondensedRegular-webfont.ttf -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/LeagueGothic-CondensedRegular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/leaguegothic/LeagueGothic-CondensedRegular-webfont.woff -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/LeagueGothic-Italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/leaguegothic/LeagueGothic-Italic-webfont.eot -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/LeagueGothic-Italic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/leaguegothic/LeagueGothic-Italic-webfont.ttf -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/LeagueGothic-Italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/leaguegothic/LeagueGothic-Italic-webfont.woff -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/LeagueGothic-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/leaguegothic/LeagueGothic-Regular-webfont.eot -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/LeagueGothic-Regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/leaguegothic/LeagueGothic-Regular-webfont.ttf -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/LeagueGothic-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/leaguegothic/LeagueGothic-Regular-webfont.woff -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/SIL OFL Font License League Gothic.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Caroline Hadilaksono & Micah Rich , with Reserved Font Name: "League Gothic". 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | Version 1.1 - 26 February 2007 8 | 9 | 10 | SIL Open Font License 11 | ==================================================== 12 | 13 | 14 | Preamble 15 | ---------- 16 | 17 | The goals of the Open Font License (OFL) are to stimulate worldwide 18 | development of collaborative font projects, to support the font creation 19 | efforts of academic and linguistic communities, and to provide a free and 20 | open framework in which fonts may be shared and improved in partnership 21 | with others. 22 | 23 | The OFL allows the licensed fonts to be used, studied, modified and 24 | redistributed freely as long as they are not sold by themselves. The 25 | fonts, including any derivative works, can be bundled, embedded, 26 | redistributed and/or sold with any software provided that any reserved 27 | names are not used by derivative works. The fonts and derivatives, 28 | however, cannot be released under any other type of license. The 29 | requirement for fonts to remain under this license does not apply 30 | to any document created using the fonts or their derivatives. 31 | 32 | Definitions 33 | ------------- 34 | 35 | `"Font Software"` refers to the set of files released by the Copyright 36 | Holder(s) under this license and clearly marked as such. This may 37 | include source files, build scripts and documentation. 38 | 39 | `"Reserved Font Name"` refers to any names specified as such after the 40 | copyright statement(s). 41 | 42 | `"Original Version"` refers to the collection of Font Software components as 43 | distributed by the Copyright Holder(s). 44 | 45 | `"Modified Version"` refers to any derivative made by adding to, deleting, 46 | or substituting -- in part or in whole -- any of the components of the 47 | Original Version, by changing formats or by porting the Font Software to a 48 | new environment. 49 | 50 | `"Author"` refers to any designer, engineer, programmer, technical 51 | writer or other person who contributed to the Font Software. 52 | 53 | Permission & Conditions 54 | ------------------------ 55 | 56 | Permission is hereby granted, free of charge, to any person obtaining 57 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 58 | redistribute, and sell modified and unmodified copies of the Font 59 | Software, subject to the following conditions: 60 | 61 | 1. Neither the Font Software nor any of its individual components, 62 | in Original or Modified Versions, may be sold by itself. 63 | 64 | 2. Original or Modified Versions of the Font Software may be bundled, 65 | redistributed and/or sold with any software, provided that each copy 66 | contains the above copyright notice and this license. These can be 67 | included either as stand-alone text files, human-readable headers or 68 | in the appropriate machine-readable metadata fields within text or 69 | binary files as long as those fields can be easily viewed by the user. 70 | 71 | 3. No Modified Version of the Font Software may use the Reserved Font 72 | Name(s) unless explicit written permission is granted by the corresponding 73 | Copyright Holder. This restriction only applies to the primary font name as 74 | presented to the users. 75 | 76 | 4. The name(s) of the Copyright Holder(s) or the Author(s) of the Font 77 | Software shall not be used to promote, endorse or advertise any 78 | Modified Version, except to acknowledge the contribution(s) of the 79 | Copyright Holder(s) and the Author(s) or with their explicit written 80 | permission. 81 | 82 | 5. The Font Software, modified or unmodified, in part or in whole, 83 | must be distributed entirely under this license, and must not be 84 | distributed under any other license. The requirement for fonts to 85 | remain under this license does not apply to any document created 86 | using the Font Software. 87 | 88 | Termination 89 | ----------- 90 | 91 | This license becomes null and void if any of the above conditions are 92 | not met. 93 | 94 | 95 | DISCLAIMER 96 | 97 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 98 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 99 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 100 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 101 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 102 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 103 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 104 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 105 | OTHER DEALINGS IN THE FONT SOFTWARE. 106 | -------------------------------------------------------------------------------- /2013-05/IPython/_static/leaguegothic/stylesheet.css: -------------------------------------------------------------------------------- 1 | /* Generated by Font Squirrel (http://www.fontsquirrel.com) on May 5, 2013 12:09:23 AM America/New_York */ 2 | 3 | 4 | 5 | @font-face { 6 | font-family: 'LeagueGothicRegular'; 7 | src: url('LeagueGothic-Regular-webfont.eot'); 8 | src: url('LeagueGothic-Regular-webfont.eot?#iefix') format('embedded-opentype'), 9 | url('LeagueGothic-Regular-webfont.woff') format('woff'), 10 | url('LeagueGothic-Regular-webfont.ttf') format('truetype'), 11 | url('LeagueGothic-Regular-webfont.svg#LeagueGothicRegular') format('svg'); 12 | font-weight: normal; 13 | font-style: normal; 14 | 15 | } 16 | 17 | @font-face { 18 | font-family: 'LeagueGothicItalic'; 19 | src: url('LeagueGothic-Italic-webfont.eot'); 20 | src: url('LeagueGothic-Italic-webfont.eot?#iefix') format('embedded-opentype'), 21 | url('LeagueGothic-Italic-webfont.woff') format('woff'), 22 | url('LeagueGothic-Italic-webfont.ttf') format('truetype'), 23 | url('LeagueGothic-Italic-webfont.svg#LeagueGothicItalic') format('svg'); 24 | font-weight: normal; 25 | font-style: normal; 26 | 27 | } 28 | 29 | @font-face { 30 | font-family: 'LeagueGothicCondensedRegular'; 31 | src: url('LeagueGothic-CondensedRegular-webfont.eot'); 32 | src: url('LeagueGothic-CondensedRegular-webfont.eot?#iefix') format('embedded-opentype'), 33 | url('LeagueGothic-CondensedRegular-webfont.woff') format('woff'), 34 | url('LeagueGothic-CondensedRegular-webfont.ttf') format('truetype'), 35 | url('LeagueGothic-CondensedRegular-webfont.svg#LeagueGothicCondensedRegular') format('svg'); 36 | font-weight: normal; 37 | font-style: normal; 38 | 39 | } 40 | 41 | @font-face { 42 | font-family: 'LeagueGothicCondensedItalic'; 43 | src: url('LeagueGothic-CondensedItalic-webfont.eot'); 44 | src: url('LeagueGothic-CondensedItalic-webfont.eot?#iefix') format('embedded-opentype'), 45 | url('LeagueGothic-CondensedItalic-webfont.woff') format('woff'), 46 | url('LeagueGothic-CondensedItalic-webfont.ttf') format('truetype'), 47 | url('LeagueGothic-CondensedItalic-webfont.svg#LeagueGothicCondensedItalic') format('svg'); 48 | font-weight: normal; 49 | font-style: normal; 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /2013-05/IPython/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/minus.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/module_path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/module_path.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/module_source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/module_source.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/plus.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/prompt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/prompt.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #eeffcc; } 3 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 8 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 9 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 10 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 11 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 14 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 15 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 16 | .highlight .go { color: #333333 } /* Generic.Output */ 17 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 18 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 19 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 20 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 21 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 22 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 23 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 24 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 25 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 26 | .highlight .kt { color: #902000 } /* Keyword.Type */ 27 | .highlight .m { color: #208050 } /* Literal.Number */ 28 | .highlight .s { color: #4070a0 } /* Literal.String */ 29 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 30 | .highlight .nb { color: #007020 } /* Name.Builtin */ 31 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 32 | .highlight .no { color: #60add5 } /* Name.Constant */ 33 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 34 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 35 | .highlight .ne { color: #007020 } /* Name.Exception */ 36 | .highlight .nf { color: #06287e } /* Name.Function */ 37 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 38 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 39 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 40 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 41 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 42 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 43 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 44 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 45 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 46 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 47 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 48 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 49 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 50 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 51 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 52 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 53 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 54 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 55 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 56 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 57 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 58 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 59 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 60 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 61 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 62 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /2013-05/IPython/_static/slides.css: -------------------------------------------------------------------------------- 1 | img.fill { 2 | position: absolute; 3 | left: 0; 4 | top: 0; 5 | min-width: 100%; 6 | min-height: 100%; 7 | 8 | border-radius: 10px; 9 | -o-border-radius: 10px; 10 | -moz-border-radius: 10px; 11 | -webkit-border-radius: 10px; 12 | 13 | z-index: -1; 14 | } 15 | 16 | a.headerlink { 17 | visibility: hidden; 18 | } 19 | 20 | h1:hover > a.headerlink, 21 | h2:hover > a.headerlink, 22 | h3:hover > a.headerlink, 23 | h4:hover > a.headerlink, 24 | h5:hover > a.headerlink, 25 | h6:hover > a.headerlink, 26 | dt:hover > a.headerlink { 27 | visibility: visible; 28 | } 29 | 30 | div.figure p.caption { 31 | position: absolute; 32 | left: 0; 33 | bottom: 0; 34 | font-size: 0.5em; 35 | } 36 | 37 | .slide-no { 38 | position: absolute; 39 | bottom: 1ex; 40 | right: 1em; 41 | font-size: 66%; 42 | } -------------------------------------------------------------------------------- /2013-05/IPython/_static/sync.js: -------------------------------------------------------------------------------- 1 | var SlideSync = ( 2 | function() { 3 | 4 | var 5 | slides, 6 | slide_listeners = [], 7 | 8 | showConsole = function(e) { 9 | slide_console = window.open( 10 | DOCUMENTATION_OPTIONS.URL_ROOT + 'console.html', 11 | 'console', 12 | "menubar=no,location=no,resizable=yes,scrollbars=yes,status=yes"); 13 | }, 14 | 15 | nextSlide = function() { 16 | slides.nextSlide(); 17 | 18 | sendCommand('nextSlide'); 19 | sendCurSlide(); 20 | }, 21 | 22 | prevSlide = function() { 23 | slides.prevSlide(); 24 | 25 | sendCommand('prevSlide'); 26 | sendCurSlide(); 27 | }, 28 | 29 | sendCurSlide = function() { 30 | 31 | var curSlide = slides.curSlide(); 32 | 33 | notifyListeners( 34 | {command: 'cur_slide', 35 | content: curSlide, 36 | prev_slide: curSlide > 0 ? slides.getSlideEl(curSlide - 1).outerHTML : '', 37 | slide: slides.getSlideEl(curSlide).outerHTML, 38 | next_slide: curSlide < slides.length() - 1 ? slides.getSlideEl(curSlide + 1).outerHTML : '' 39 | } 40 | ); 41 | 42 | }, 43 | 44 | notifyListeners = function (message) { 45 | 46 | for (var i = 0; i < slide_listeners.length; i++) { 47 | slide_listeners[i].postMessage(message, '*'); 48 | } 49 | 50 | }, 51 | 52 | sendCommand = function(command) { 53 | return sendMessage({'command':command}); 54 | }, 55 | 56 | sendMessage = function(message) { 57 | notifyListeners(message); 58 | }, 59 | 60 | handleMessage = function(message, source) { 61 | 62 | console.log(message); 63 | 64 | switch (message.command) { 65 | 66 | case 'register': 67 | slide_listeners.push(source); 68 | sendMessage( 69 | {command: 'num_slides', 70 | content: slideEls.length 71 | } 72 | ); 73 | break; 74 | 75 | case 'nextSlide': 76 | slides.nextSlide(); 77 | break; 78 | 79 | case 'prevSlide': 80 | slides.prevSlide(); 81 | break; 82 | 83 | }; 84 | 85 | sendCurSlide(); 86 | 87 | }, 88 | 89 | onKeyDown = function (event) { 90 | 91 | switch (event.keyCode) { 92 | 93 | case 84: // t 94 | slides.toggleView && slides.toggleView(); 95 | break; 96 | 97 | case 67: // c 98 | showConsole(); 99 | break; 100 | } 101 | }, 102 | 103 | init = function(slidedeck) { 104 | slides = slidedeck; 105 | 106 | // attach event handlers 107 | document.addEventListener('keydown', onKeyDown, false); 108 | window.addEventListener( 109 | 'message', 110 | function(e) { 111 | return handleMessage(e.data, e.source); 112 | }, false); 113 | 114 | }; 115 | 116 | 117 | return { 118 | init: init, 119 | showConsole: showConsole, 120 | 121 | nextSlide: nextSlide, 122 | prevSlide: prevSlide, 123 | 124 | handleMessage: handleMessage, 125 | sendMessage: sendMessage, 126 | sendCommand: sendCommand 127 | }; 128 | 129 | }()); -------------------------------------------------------------------------------- /2013-05/IPython/_static/theme.css: -------------------------------------------------------------------------------- 1 | a.headerlink { 2 | display: none; 3 | } -------------------------------------------------------------------------------- /2013-05/IPython/_static/up-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/up-pressed.png -------------------------------------------------------------------------------- /2013-05/IPython/_static/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-05/IPython/_static/up.png -------------------------------------------------------------------------------- /2013-05/threading/README.md: -------------------------------------------------------------------------------- 1 | Threading Patterns in Python 2 | ============================ 3 | JR Rickerson 4 | 5 | Why Threading? 6 | -------------- 7 | Sometimes, it makes sense to break a program down such that we can do little 8 | pieces of it at a time, and some of those pieces can be done in parallel. It's 9 | not a guarentee, but sometimes this can save us time overall, providing faster 10 | processing of large amounts of information or faster user response time. 11 | 12 | In this talk, we'll specifically be looking at threading. However, many of 13 | the same concepts apply to multi-process applications, or even applications 14 | distributed across many machine in a cluster, or across the world. 15 | 16 | Why Threading Patterns? 17 | ----------------------- 18 | When we introduct threading or any kind of parallelism to an application, it 19 | can help us solve a certain set of problems - however, it can introduce a whole 20 | new set of problems as well. When different threads of execution are sharing 21 | the same resources - be it actual memory (variables, data structures), 22 | communication channels (pipes, sockets), or stored information (files, 23 | databases), unless we have a plan in place for coordinating access to these 24 | resources, unintended behavior can occur. 25 | 26 | Remember - the interpreter doesn't know what you intended, and the OS doesn't 27 | have any concept of what order to execute statements in your threads in - 28 | UNLESS you tell it. 29 | 30 | Most threading libraries, including Python's, provide what are called 31 | "synchronization" objects. Using these, in conjuction with well-established 32 | patterns for parallel process, can help us avoid or at least minimize many of 33 | the issues we could run into in the multi-threaded world. 34 | 35 | Most of these patterns have been established because they continually crop up 36 | as good solutions to common problems where multi-threading is useful, and 37 | through refinement of techniques by programmers writing multi-threaded code 38 | for years. 39 | 40 | References 41 | ---------- 42 | * Python threading module: http://docs.python.org/2/library/threading.html 43 | * PMOTW threading: http://pymotw.com/2/threading/ 44 | * Queue module: http://docs.python.org/2/library/queue.html 45 | 46 | -------------------------------------------------------------------------------- /2013-05/threading/barrier.py: -------------------------------------------------------------------------------- 1 | # Threading Patterns in Python 2 | # 3 | # The "Barrier" pattern. 4 | # This pattern is used when you need to have multiple threads doing work, 5 | # and additional work needs to be completed, but only AFTER the other threads 6 | # have first completed their work. You can think of it like a dependency tree, 7 | # like so: 8 | # 9 | # 10 | # --> Task A --> 11 | # Start --> --> Task B --> --> Task D --> Complete 12 | # --> Task C --> 13 | 14 | import time 15 | from threading import Thread, Event 16 | 17 | A = Event() 18 | B = Event() 19 | C = Event() 20 | D = Event() 21 | 22 | def taskA(): 23 | print 'Task A is doing some work' 24 | time.sleep(4) 25 | A.set() 26 | D.wait() 27 | print 'Task A is exiting' 28 | 29 | def taskB(): 30 | print 'Task B is doing some work' 31 | time.sleep(1) 32 | B.set() 33 | D.wait() 34 | print 'Task B is exiting' 35 | 36 | def taskC(): 37 | print 'Task C is doing some work' 38 | time.sleep(3) 39 | C.set() 40 | D.wait() 41 | print 'Task C is exiting' 42 | 43 | def taskD(): 44 | print 'Task D is waiting on other tasks' 45 | A.wait() 46 | B.wait() 47 | C.wait() 48 | print 'Task D is doing some work' 49 | time.sleep(1) 50 | D.set() 51 | print 'Task D is exiting' 52 | 53 | 54 | def main(): 55 | 56 | # Create our threads 57 | tasks = [ 58 | Thread(target=taskA), 59 | Thread(target=taskB), 60 | Thread(target=taskC), 61 | Thread(target=taskD), 62 | ] 63 | 64 | print '"Main" is starting threads' 65 | # Start all the threads running 66 | for task in tasks: 67 | task.start() 68 | 69 | print '"Main" is waiting for threads to finish' 70 | for task in tasks: 71 | task.join() 72 | 73 | 74 | if __name__ == '__main__': 75 | main() 76 | 77 | -------------------------------------------------------------------------------- /2013-05/threading/callback.py: -------------------------------------------------------------------------------- 1 | # Threading Patterns in Python 2 | # 3 | # The "Callback" pattern. 4 | # This pattern is not strictly a parallel / threading pattern, but often 5 | # comes up in asychronous development or multi-threading scenarios. 6 | # This is also common in scenarios when using a third party library that 7 | # you may have little to no control over. 8 | # Some thread typically runs as an "engine" or main thread, doing various 9 | # types of processing. Other threads under your control are able to register 10 | # callback functions which are called when certain events occur during the main 11 | # threading processing. 12 | # 13 | # 14 | # Start --> Engine (loops) --> Complete 15 | # | 16 | # ---> Callback function 17 | # 18 | 19 | import time 20 | from threading import Thread 21 | 22 | 23 | def engine(fizz_callback, buzz_callback): 24 | for i in xrange(100): 25 | # Engine does some work 26 | time.sleep(1) 27 | if i % 3 == 0: 28 | fizz_callback(i) 29 | if i % 5 == 0: 30 | callback_thread = Thread(target=buzz_callback, args=(i,)) 31 | callback_thread.start() 32 | 33 | def fizz_handler(num): 34 | print 'Value {0} is FIZZ'.format(num) 35 | 36 | def buzz_handler(num): 37 | print 'Value {0} is BUZZ'.format(num) 38 | 39 | def main(): 40 | engine_thread = Thread(target=engine, args=(fizz_handler, buzz_handler)) 41 | 42 | engine_thread.start() 43 | engine_thread.join() 44 | 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /2013-05/threading/producer_consumer.py: -------------------------------------------------------------------------------- 1 | # Threading Patterns in Python 2 | # 3 | # The Producer-Consumer Pattern 4 | # This is a very common pattern in multi-threading scenarios. In this pattern, one or 5 | # more threads are doing some pre-processing and "producing" units of work, while another 6 | # set of "consumer" or "worker" threads are actually doing the processing on these units 7 | # of work. Often, a queue system is used to communicate between threads and provide 8 | # synchronization. These queue-linked producers and consumers can be chained together 9 | # to form a type of pipeline. The queue can also set a maximum size to limit how much 10 | # work is done at once. 11 | 12 | import time 13 | from threading import Thread, Event 14 | from Queue import Queue, Empty 15 | 16 | producers_complete = Event() 17 | consumers_complete = Event() 18 | 19 | source_queue = Queue() 20 | work_queue = Queue() 21 | 22 | 23 | def consumer(consumers_complete): 24 | print 'Consumer started.' 25 | while not consumers_complete.is_set(): 26 | try: 27 | item = work_queue.get(True, 1) 28 | print 'Consumer is working on {0}'.format(item) 29 | time.sleep(1) 30 | work_queue.task_done() 31 | except Empty: 32 | print 'Consumer Work Queue was empty, no work to do.' 33 | 34 | print 'Consumer exited.' 35 | 36 | def producer(producers_complete): 37 | print 'Producer started.' 38 | while not producers_complete.is_set(): 39 | try: 40 | number = source_queue.get(True, 1) 41 | print 'Producer is generating work for item #{0}'.format(number) 42 | work_queue.put('Work Item #{0}'.format(number)) 43 | source_queue.task_done() 44 | except Empty: 45 | print 'Producer Source Queue was empty, no work to do.' 46 | print 'Producer exited.' 47 | 48 | 49 | def main(): 50 | # Set up initial signal conditions 51 | 52 | # Create 5 consumers 53 | consumers = [Thread(target=consumer, args=(consumers_complete,)) for i in range(5)] 54 | # Create 3 producers 55 | producers = [Thread(target=producer, args=(producers_complete,)) for i in range(3)] 56 | 57 | # Provide some original source of work 58 | for i in xrange(100): 59 | source_queue.put(i + 1) 60 | 61 | # Start consumers and producers 62 | for c in consumers: 63 | c.start() 64 | for p in producers: 65 | p.start() 66 | 67 | # Wait until all the work has been picked up 68 | source_queue.join() 69 | # Signal all our producers threads to exit 70 | producers_complete.set() 71 | 72 | # Wait on all the work to be completed 73 | work_queue.join() 74 | consumers_complete.set() 75 | 76 | # Now wait on all the threads to exit 77 | for p in producers: 78 | p.join() 79 | for c in consumers: 80 | c.join() 81 | 82 | 83 | if __name__ == '__main__': 84 | main() 85 | -------------------------------------------------------------------------------- /2013-06/fizzbuzz/01_fizz_or_buzz.py: -------------------------------------------------------------------------------- 1 | 'Fizz' if not n % 3 2 | 'Buzz' if not n % 5 3 | -------------------------------------------------------------------------------- /2013-06/fizzbuzz/02_fizz_and_buzz.py: -------------------------------------------------------------------------------- 1 | (''.join( 2 | ['Fizz' if not n % 3 else '', 3 | 'Buzz' if not n % 5 else '']) 4 | ) 5 | -------------------------------------------------------------------------------- /2013-06/fizzbuzz/03_number.py: -------------------------------------------------------------------------------- 1 | ( 2 | ''.join( 3 | ['Fizz' if not n % 3 else '', 4 | 'Buzz' if not n % 5 else '']) 5 | 6 | or str(n)) 7 | ) 8 | -------------------------------------------------------------------------------- /2013-06/fizzbuzz/boring.py: -------------------------------------------------------------------------------- 1 | for i in xrange(101): 2 | if (not i % 3) and (not i % 5): 3 | print 'FizzBuzz' 4 | elif not i % 3: 5 | print 'Fizz' 6 | elif not i % 5: 7 | print 'Buzz' 8 | else: 9 | print i 10 | -------------------------------------------------------------------------------- /2013-06/fizzbuzz/clever.py: -------------------------------------------------------------------------------- 1 | for i in xrange(101): 2 | out = [] 3 | if not i % 3: 4 | out.append('Fizz') 5 | if not i % 5: 6 | out.append('Buzz') 7 | if not out: 8 | out.append(str(i)) 9 | print ''.join(out) 10 | -------------------------------------------------------------------------------- /2013-06/fizzbuzz/fizzbuzz.py: -------------------------------------------------------------------------------- 1 | print '\n'.join( 2 | (''.join( 3 | ['Fizz' if not n % 3 else '', 4 | 'Buzz' if not n % 5 else '']) 5 | or str(n)) 6 | for n in range(1, 101) 7 | ) 8 | -------------------------------------------------------------------------------- /2013-06/fizzbuzz/fizzbuzz_tweet.py: -------------------------------------------------------------------------------- 1 | print '\n'.join((''.join(['Fizz' if not n % 3 else '', 'Buzz' if not n % 5 else '']) or str(n)) for n in range(1, 101)) 2 | -------------------------------------------------------------------------------- /2013-06/fizzbuzz/one-liner.sh: -------------------------------------------------------------------------------- 1 | # via http://www.commandlinefu.com/commands/view/9454/fizzbuzz-one-liner-in-python 2 | 3 | python -c 'for i in range(1,101): print"FizzBuzz"[i*i%3*4:8--i**4%5]or i' 4 | 5 | -------------------------------------------------------------------------------- /2013-07/virtualenv/requirements.txt: -------------------------------------------------------------------------------- 1 | Jinja2==2.7 2 | MarkupSafe==0.18 3 | Pygments==1.6 4 | Sphinx==1.2b1 5 | docutils==0.10 6 | hieroglyph==0.5.5 7 | -------------------------------------------------------------------------------- /2013-07/virtualenv/source/_static/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-07/virtualenv/source/_static/.placeholder -------------------------------------------------------------------------------- /2013-07/virtualenv/source/_static/custom.css: -------------------------------------------------------------------------------- 1 | .slides > article { 2 | /* background-color: lightblue; */ 3 | color: #222222; 4 | } 5 | 6 | h2 { 7 | bottom: 50px; 8 | } 9 | 10 | /* Use when the pre tag contents do not fit on the screen otherwise. */ 11 | .small { 12 | font-size: 70%; 13 | } 14 | -------------------------------------------------------------------------------- /2013-07/virtualenv/source/_templates/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-07/virtualenv/source/_templates/.placeholder -------------------------------------------------------------------------------- /2013-07/virtualenv/source/virtualenv_output.txt: -------------------------------------------------------------------------------- 1 | $ virtualenv -v foo 2 | Creating foo/lib/python2.7 3 | Symlinking Python bootstrap modules 4 | Symlinking foo/lib/python2.7/config 5 | Symlinking foo/lib/python2.7/lib-dynload 6 | Symlinking foo/lib/python2.7/os.py 7 | Ignoring built-in bootstrap module: posix 8 | Symlinking foo/lib/python2.7/posixpath.py 9 | Cannot import bootstrap module: nt 10 | Symlinking foo/lib/python2.7/ntpath.py 11 | Symlinking foo/lib/python2.7/genericpath.py 12 | Symlinking foo/lib/python2.7/fnmatch.py 13 | Symlinking foo/lib/python2.7/locale.py 14 | Symlinking foo/lib/python2.7/encodings 15 | Symlinking foo/lib/python2.7/codecs.py 16 | Symlinking foo/lib/python2.7/stat.py 17 | Symlinking foo/lib/python2.7/UserDict.py 18 | Symlinking foo/lib/python2.7/copy_reg.py 19 | Symlinking foo/lib/python2.7/types.py 20 | Symlinking foo/lib/python2.7/re.py 21 | Symlinking foo/lib/python2.7/sre.py 22 | Symlinking foo/lib/python2.7/sre_parse.py 23 | Symlinking foo/lib/python2.7/sre_constants.py 24 | Symlinking foo/lib/python2.7/sre_compile.py 25 | Symlinking foo/lib/python2.7/warnings.py 26 | Symlinking foo/lib/python2.7/linecache.py 27 | Symlinking foo/lib/python2.7/_abcoll.py 28 | Symlinking foo/lib/python2.7/abc.py 29 | Symlinking foo/lib/python2.7/_weakrefset.py 30 | Creating foo/lib/python2.7/site-packages 31 | Writing foo/lib/python2.7/site.py 32 | Writing foo/lib/python2.7/orig-prefix.txt 33 | Writing foo/lib/python2.7/no-global-site-packages.txt 34 | Creating parent directories for foo/include 35 | Symlinking foo/include/python2.7 36 | Creating foo/bin 37 | New python executable in foo/bin/python 38 | Changed mode of foo/bin/python to 0755 39 | Symlinking foo/.Python 40 | Testing executable with foo/bin/python -c "import sys;out=sys.stdout;getattr(out, "buffer", out).write(sys.prefix.encode("utf-8"))" 41 | Got sys.prefix result: u'/Users/dhellmann/Documents/2013/Presentations/virtualenv/foo' 42 | Creating foo/lib/python2.7/distutils 43 | Writing foo/lib/python2.7/distutils/__init__.py 44 | Writing foo/lib/python2.7/distutils/distutils.cfg 45 | Using existing setuptools egg: /Users/dhellmann/Library/Python/2.7/lib/python/site-packages/virtualenv_support/setuptools-0.6c11-py2.7.egg 46 | Installing setuptools.......... 47 | Processing dependencies for setuptools==0.6c11 48 | Finished processing dependencies for setuptools==0.6c11 49 | ...Installing setuptools...done. 50 | Installing existing pip-1.3.1.tar.gz distribution: /Users/dhellmann/Library/Python/2.7/lib/python/site-packages/virtualenv_support/pip-1.3.1.tar.gz 51 | Installing pip... 52 | Processing pip-1.3.1.tar.gz 53 | Running pip-1.3.1/setup.py -q bdist_egg --dist-dir /var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T/easy_install-Jsz3Pu/pip-1.3.1/egg-dist-tmp-3vXRNj 54 | warning: no files found matching '*.html' under directory 'docs' 55 | warning: no previously-included files matching '*.txt' found under directory 'docs/_build' 56 | no previously-included directories found matching 'docs/_build/_sources' 57 | Adding pip 1.3.1 to easy-install.pth file 58 | Processing dependencies for pip==1.3.1 59 | Finished processing dependencies for pip==1.3.1 60 | ...Installing pip...done. 61 | Writing foo/bin/activate 62 | Writing foo/bin/activate.fish 63 | Writing foo/bin/activate_this.py 64 | Writing foo/bin/activate.csh 65 | -------------------------------------------------------------------------------- /2013-09/operator-overloading/source/_static/custom.css: -------------------------------------------------------------------------------- 1 | .slides > article { 2 | /* background-color: lightblue; */ 3 | color: #222222; 4 | } 5 | 6 | h2 { 7 | bottom: 50px; 8 | } 9 | 10 | /* Use when the pre tag contents do not fit on the screen otherwise. */ 11 | .small { 12 | font-size: 70%; 13 | } 14 | -------------------------------------------------------------------------------- /2013-09/operator-overloading/source/index.rst: -------------------------------------------------------------------------------- 1 | =================================================== 2 | Operator Overloading and Python's Special Methods 3 | =================================================== 4 | 5 | | PyATL 6 | | September, 2013 7 | | Doug.Hellmann@gmail.com 8 | | @doughellmann 9 | | http://doughellmann.com 10 | 11 | .. note:: 12 | 13 | Going to talk about: 14 | 15 | 1. What special methods are. 16 | 2. Some example special methods. 17 | 3. Look at how the comparison operators use special methods. 18 | 19 | What are "Special Methods"? 20 | =========================== 21 | 22 | *Special Methods* are a naming convention to bind methods of your 23 | class to operators or built-ins in the language. 24 | 25 | Also known as "Dunder" methods ("double underscore"). 26 | 27 | .. note:: 28 | 29 | Special methods tie your class in to Python by using well-defined 30 | names that the interpreter looks for. 31 | 32 | __init__ 33 | ======== 34 | 35 | :: 36 | 37 | my_inst = MyClass() 38 | 39 | When a Class type needs to initialize a new instance, it calls the 40 | __init__ method. 41 | 42 | __del__ 43 | ======= 44 | 45 | :: 46 | 47 | del my_inst 48 | 49 | Clean up resources owned by an object in __del__. 50 | 51 | __str__ and __unicode__ 52 | ======================= 53 | 54 | :: 55 | 56 | str(my_inst) 57 | unicode(my_inst) 58 | 59 | Replacing the string representation via __str__ or __unicode__ makes 60 | it easier to debug with custom classes. 61 | 62 | __repr__ 63 | ======== 64 | 65 | :: 66 | 67 | repr(my_inst) 68 | print my_inst 69 | 70 | The print statement uses the repr() of an object by default. 71 | 72 | __iter__ 73 | ======== 74 | 75 | :: 76 | 77 | for i in my_inst: 78 | ... 79 | 80 | The __iter__ method returns an iterator for an iterable object. 81 | 82 | __call__ 83 | ======== 84 | 85 | :: 86 | 87 | my_inst(arg1, arg2) 88 | 89 | Adding a __call__ method means you can use the "invoke function" 90 | operator of two parens. 91 | 92 | 93 | __getattr__, __setattr__, __delattr__ 94 | ===================================== 95 | 96 | :: 97 | 98 | print my_inst.foo 99 | my_inst.foo = 'value' 100 | del my_inst.foo 101 | 102 | Attribute access using the dot operator can be overridden, too. 103 | 104 | __enter__ and __exit__ 105 | ====================== 106 | 107 | :: 108 | 109 | with my_inst as context: 110 | do_something(context) 111 | 112 | Adding __enter__ and __exit__ makes your class work as a context 113 | manager, so you can use it with the "with" statement. 114 | 115 | __len__ 116 | ======= 117 | 118 | :: 119 | 120 | len(my_inst) 121 | 122 | The len() built-in invokes the __len__ method 123 | 124 | __hash__ 125 | ======== 126 | 127 | :: 128 | 129 | hash(my_inst) 130 | 131 | d = {my_inst: 'value'} 132 | 133 | s = {my_inst} 134 | 135 | To control the way dictionary keys and set membership is computed, 136 | override the __hash__ method. 137 | 138 | __nonzero__ 139 | =========== 140 | 141 | :: 142 | 143 | if my_inst: 144 | do_something() 145 | 146 | __nonzero__ is called to convert an object to a boolean 147 | representation 148 | 149 | __contains__ 150 | ============ 151 | 152 | :: 153 | 154 | if 'foo' in my_inst: 155 | do_something() 156 | 157 | if 'bar' not in my_inst: 158 | do_something_else() 159 | 160 | Python will fall back to scanning data structures, but using ``in`` to 161 | test membership can make it more efficient. 162 | 163 | Old-Style Comparison 164 | ==================== 165 | 166 | ``__cmp__()`` returns a number representing the relationship. 167 | 168 | :-1: The object is less than the argument. 169 | :0: The object is equal to the argument. 170 | :1: The object is greater than the argument. 171 | 172 | **Warning**: This comparison style is deprecated. 173 | 174 | Rich Comparison Methods 175 | ======================= 176 | 177 | ======== ========== 178 | Method Operator 179 | ======== ========== 180 | __lt__ ``<`` 181 | __le__ ``<=`` 182 | __eq__ ``==`` 183 | __ne__ ``!=`` 184 | __gt__ ``>`` 185 | __ge__ ``>=`` 186 | ======== ========== 187 | 188 | ``functools.total_ordering`` 189 | 190 | Default Sorting 191 | =============== 192 | 193 | .. literalinclude:: unsorted.py 194 | :lines: 4-11 195 | 196 | Default Sorting 197 | =============== 198 | 199 | .. literalinclude:: unsorted.py 200 | :lines: 14-22 201 | 202 | *What will the output be?* 203 | 204 | Default Sorting 205 | =============== 206 | 207 | :: 208 | 209 | $ python unsorted.py 210 | 211 | Jetson, George 212 | Jetson, Jane 213 | Jetson, Elroy 214 | Jetson, Judy 215 | 216 | *Why?* 217 | 218 | Custom Sorting 219 | ============== 220 | 221 | .. literalinclude:: sortable.py 222 | :lines: 6-24 223 | :emphasize-lines: 1,10-24 224 | 225 | .. note:: 226 | 227 | ``total_ordering`` adds the missing comparison operations based on 228 | the 2 given. 229 | 230 | Custom Sorting 231 | ============== 232 | 233 | :: 234 | 235 | $ python sortable.py 236 | 237 | Jetson, Elroy 238 | Jetson, George 239 | Jetson, Jane 240 | Jetson, Judy 241 | 242 | Caveats 243 | ======= 244 | 245 | - ``total_ordering`` requires two rich comparison methods. 246 | - Easy to get carried away and abuse operator overloading. 247 | - Python does not do any type checking for you. 248 | - Special method names are reserved for internal use, so don't name 249 | arbitrary methods using the dunder scheme. 250 | 251 | References 252 | ========== 253 | 254 | - "Special Method Names" 255 | http://docs.python.org/2/reference/datamodel.html#special-method-names 256 | -------------------------------------------------------------------------------- /2013-09/operator-overloading/source/sortable.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | 3 | import functools 4 | 5 | 6 | @functools.total_ordering 7 | class Person(object): 8 | 9 | def __init__(self, first, last): 10 | self.first = first 11 | self.last = last 12 | 13 | def __repr__(self): 14 | return ', '.join([self.last, self.first]) 15 | 16 | def __eq__(self, other): 17 | if not isinstance(other, Person): 18 | return NotImplemented 19 | return (self.last, self.first) == (other.last, other.first) 20 | 21 | def __lt__(self, other): 22 | if not isinstance(other, Person): 23 | return NotImplemented 24 | return (self.last, self.first) < (other.last, other.first) 25 | 26 | 27 | data = [ 28 | Person('George', 'Jetson'), 29 | Person('Jane', 'Jetson'), 30 | Person('Elroy', 'Jetson'), 31 | Person('Judy', 'Jetson'), 32 | ] 33 | 34 | for p in sorted(data): 35 | print p 36 | -------------------------------------------------------------------------------- /2013-09/operator-overloading/source/unsorted.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | 3 | 4 | class Person(object): 5 | 6 | def __init__(self, first, last): 7 | self.first = first 8 | self.last = last 9 | 10 | def __repr__(self): 11 | return ', '.join([self.last, self.first]) 12 | 13 | 14 | data = [ 15 | Person('George', 'Jetson'), 16 | Person('Jane', 'Jetson'), 17 | Person('Elroy', 'Jetson'), 18 | Person('Judy', 'Jetson'), 19 | ] 20 | 21 | for p in sorted(data): 22 | print p 23 | -------------------------------------------------------------------------------- /2013-09/zmq/README.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | ØMQ Examples 3 | ============ 4 | 5 | :author: Daniel J. Rocco, @drocco007 6 | :date: September 2013 7 | 8 | Example code for my September 2013 PyAtl talk *Let's Talk*. 9 | 10 | `Talk slides `_; note that the 11 | background looks pretty good at projector resolution but a little weird on a 12 | full resolution display. :/ 13 | 14 | 15 | hello 16 | ===== 17 | 18 | Illustrates a basic request-response communication mechanism. Pretty much a 19 | straight copy from the guide example: http://zguide.zeromq.org/py:hwclient 20 | 21 | Run with:: 22 | 23 | python hwserver.py 24 | 25 | and in a separate window 26 | 27 | :: 28 | 29 | python hwclient.py 30 | 31 | 32 | fizzbuzz (*) 33 | ============ 34 | 35 | Very contrived publish-subscribe example. 36 | 37 | :: 38 | 39 | python ventilator.py 40 | 41 | starts the publisher, which publishes the numbers 1 to 100 forever. Yes. Time 42 | it if you don't believe me. 43 | 44 | Start the clients with something like 45 | 46 | :: 47 | 48 | python fizzer.py & python buzzer.py & 49 | 50 | Although this example is highly goofy, it does illustrate the basic principles 51 | of publish-subscribe in ØMQ. Remember: 52 | 53 | 1. Publishers do not wait. If you want synchronization, you need to add it. 54 | 55 | 2. Subscribers *must* set a subscription, even if it's ``''`` (everything). If 56 | you forget this, your listeners will be surprisingly deaf (like my kids). 57 | 58 | (*) not a real FIZZBUZZ 59 | 60 | 61 | image_farm 62 | ========== 63 | 64 | ``image_farm`` illustrates work distribution and processing, in this case 65 | resizing images in batch. The workers (``farmer.py``) receive source images and 66 | generate three size of thumbnails for each image they receive. Each worker 67 | sends a message to a listening process (``sink.py``) when finished. 68 | 69 | To try it out, first fire up some workers:: 70 | 71 | python farmer.py # want >1 of these 72 | python farmer.py 73 | python farmer.py 74 | 75 | and a sink 76 | 77 | :: 78 | 79 | python sink.py 80 | 81 | Then, queue up some images:: 82 | 83 | python cli_enqueue_images.py image1.png image2.jpg ... 84 | 85 | You probably want to give it quite a few images: modern hardware is quite fast. 86 | You'll need PIL installed for this to work; I suggest installing the Pillow 87 | fork unless you enjoy debugging build problems from an archaic package:: 88 | 89 | pip install Pillow 90 | -------------------------------------------------------------------------------- /2013-09/zmq/fizzbuzz/buzzer.py: -------------------------------------------------------------------------------- 1 | from fb import fizzerbuzzer 2 | 3 | fizzerbuzzer(5, 'Buzz') 4 | -------------------------------------------------------------------------------- /2013-09/zmq/fizzbuzz/fb.py: -------------------------------------------------------------------------------- 1 | import zmq 2 | 3 | # Awkward factory for setting up listener processes. sentinel is the "divisible 4 | # by" value we're looking for and message is the message we print out. Usually 5 | # called with 3, 'FIZZ' and 5, 'BUZZ'. 6 | 7 | def fizzerbuzzer(sentinel, message): 8 | # Socket to talk to message publisher 9 | context = zmq.Context() 10 | socket = context.socket(zmq.SUB) 11 | socket.connect("tcp://localhost:5555") 12 | 13 | socket.setsockopt(zmq.SUBSCRIBE, '') 14 | 15 | # Synchronization: we'll most likely connect in the middle of the 16 | # publisher's message cycle, so wait till we see '100' before starting. 17 | while True: 18 | if socket.recv() == '100': 19 | break 20 | 21 | # Process 100 updates 22 | for index in range(100): 23 | value = socket.recv() 24 | value = int(value) 25 | 26 | if not value % sentinel: 27 | print message 28 | else: 29 | print value 30 | -------------------------------------------------------------------------------- /2013-09/zmq/fizzbuzz/fizzer.py: -------------------------------------------------------------------------------- 1 | from fb import fizzerbuzzer 2 | 3 | fizzerbuzzer(3, 'Fizz') 4 | -------------------------------------------------------------------------------- /2013-09/zmq/fizzbuzz/ventilator.py: -------------------------------------------------------------------------------- 1 | from itertools import cycle 2 | import time 3 | 4 | import zmq 5 | 6 | context = zmq.Context() 7 | socket = context.socket(zmq.PUB) 8 | socket.bind("tcp://*:5555") 9 | 10 | for index in cycle(range(1, 101)): 11 | socket.send(str(index)) 12 | time.sleep(0.05) 13 | -------------------------------------------------------------------------------- /2013-09/zmq/hello/hwclient.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # 3 | # Hello World client in Python 4 | # Connects REQ socket to tcp://localhost:5555 5 | # Sends "Hello" to server, expects "World" back 6 | # 7 | import zmq 8 | 9 | context = zmq.Context() 10 | 11 | # Socket to talk to server 12 | print "Connecting to hello world server…" 13 | socket = context.socket(zmq.REQ) 14 | socket.connect ("tcp://localhost:5555") 15 | 16 | # Do 10 requests, waiting each time for a response 17 | for request in range (10): 18 | print "Sending request ", request,"…" 19 | socket.send ("Hello") 20 | 21 | # Get the reply. 22 | message = socket.recv() 23 | print "Received reply ", request, "[", message, "]" 24 | -------------------------------------------------------------------------------- /2013-09/zmq/hello/hwserver.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # 3 | # Hello World server in Python 4 | # Connects REP socket to tcp://localhost:5555 5 | # Receives "Hello" from client, sends "World" back 6 | 7 | from time import time 8 | import zmq 9 | 10 | context = zmq.Context() 11 | socket = context.socket(zmq.REP) 12 | socket.bind("tcp://*:5555") 13 | 14 | while True: 15 | message = socket.recv() 16 | print "[{}] Received {}".format(time(), message) 17 | socket.send("World") 18 | -------------------------------------------------------------------------------- /2013-09/zmq/image_farm/cli_enqueue_images.py: -------------------------------------------------------------------------------- 1 | # Task ventilator 2 | # Binds PUSH socket to tcp://localhost:5557 3 | # Sends batch of tasks to workers via that socket 4 | 5 | import sys 6 | from time import sleep 7 | 8 | import zmq 9 | 10 | context = zmq.Context() 11 | 12 | # Socket to send messages on 13 | sender = context.socket(zmq.PUSH) 14 | sender.bind("tcp://*:5557") 15 | 16 | for filename in sys.argv[1:]: 17 | with open(filename, 'rb') as f: 18 | payload = filename, f.read() 19 | 20 | # Why, yes: Python *is* awesome! 21 | sender.send_pyobj(payload) 22 | 23 | sleep(0.25) 24 | -------------------------------------------------------------------------------- /2013-09/zmq/image_farm/farmer.py: -------------------------------------------------------------------------------- 1 | # Task worker 2 | # Connects PULL socket to tcp://localhost:5557 3 | # Collects workloads from ventilator via that socket 4 | # Connects PUSH socket to tcp://localhost:5558 5 | # Sends results to sink via that socket 6 | # 7 | # Based on an example by Lev Givon 8 | 9 | import sys 10 | import time 11 | 12 | import zmq 13 | 14 | from resize_image import format_output_filename, resize_image 15 | 16 | context = zmq.Context() 17 | 18 | # Socket to receive messages on 19 | receiver = context.socket(zmq.PULL) 20 | receiver.connect("tcp://localhost:5557") 21 | 22 | # Socket to send messages to 23 | sender = context.socket(zmq.PUSH) 24 | sender.connect("tcp://localhost:5558") 25 | 26 | # Process tasks forever 27 | while True: 28 | image_name, image_data = receiver.recv_pyobj() 29 | 30 | # Simple progress indicator for the viewer 31 | sys.stdout.write('.') 32 | sys.stdout.flush() 33 | 34 | # Do the work 35 | for size in (64, 128, 256): 36 | output_filename = format_output_filename(image_name, size) 37 | resize_image(image_data, size, output_filename) 38 | 39 | # Send results to sink 40 | sender.send(output_filename) 41 | -------------------------------------------------------------------------------- /2013-09/zmq/image_farm/resize_image.py: -------------------------------------------------------------------------------- 1 | import os 2 | from StringIO import StringIO 3 | 4 | from PIL import Image 5 | 6 | 7 | def format_output_filename(input_filename, max_height): 8 | base, extension = os.path.splitext(input_filename) 9 | output_filename = ''.join([base, '_thumbnail', str(max_height), extension]) 10 | 11 | return output_filename 12 | 13 | 14 | def resize_image(image_data, max_height, output_filename): 15 | size = max_height, max_height 16 | 17 | image = Image.open(StringIO(image_data)) 18 | image.thumbnail(size, Image.ANTIALIAS) 19 | 20 | image.save(output_filename) 21 | 22 | return image 23 | 24 | 25 | # test code that can be run standalone from the command line 26 | 27 | if __name__ == '__main__': 28 | import sys 29 | 30 | max_height = 128 31 | input_filename = sys.argv[1] 32 | 33 | output_filename = format_output_filename(input_filename, max_height) 34 | 35 | with open(input_filename, 'rb') as f: 36 | image_data = f.read() 37 | 38 | image = resize_image(image_data, max_height, output_filename) 39 | 40 | print 'Resized {} ({} {} {})'.format( 41 | input_filename, image.format, image.size, image.mode) 42 | -------------------------------------------------------------------------------- /2013-09/zmq/image_farm/sink.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from time import time 3 | 4 | import zmq 5 | 6 | context = zmq.Context() 7 | 8 | # Socket to receive messages on 9 | receiver = context.socket(zmq.PULL) 10 | receiver.bind("tcp://*:5558") 11 | 12 | while True: 13 | image_name = receiver.recv() 14 | print '[{}] {} generated'.format(time(), image_name) 15 | -------------------------------------------------------------------------------- /2013-10/flake8/README.rst: -------------------------------------------------------------------------------- 1 | To build the slides, run "make". 2 | 3 | The output will be written to the "build" directory. 4 | 5 | Output 6 | ====== 7 | 8 | The output lines are formatted to make it possible (easy?) to 9 | integrate the output with an IDE or editor that knows how to jump to 10 | compiler errors. 11 | 12 | * filename 13 | * line number 14 | * column 15 | * error message 16 | 17 | -------------------------------------------------------------------------------- /2013-10/flake8/source/_static/custom.css: -------------------------------------------------------------------------------- 1 | .slides > article { 2 | /* background-color: lightblue; */ 3 | color: #222222; 4 | } 5 | 6 | h2 { 7 | bottom: 50px; 8 | } 9 | 10 | /* Use when the pre tag contents do not fit on the screen otherwise. */ 11 | .small { 12 | font-size: 70%; 13 | } 14 | 15 | /* Center block quotes vertically and horizontally. Based on 16 | http://css-tricks.com/vertically-center-multi-lined-text/ 17 | */ 18 | 19 | blockquote { 20 | font-size: 130%; 21 | display: table; 22 | vertical-align: middle; 23 | text-align: center; 24 | height: 80%; 25 | width: 80%; 26 | } 27 | 28 | blockquote div { 29 | display: table-cell; 30 | vertical-align: middle; 31 | text-align: center; 32 | } -------------------------------------------------------------------------------- /2013-10/flake8/source/clean.py: -------------------------------------------------------------------------------- 1 | MAPPING = { 2 | 'a': 'A', 3 | } 4 | 5 | LIST = [ 6 | 'a', 7 | 'b', 8 | ] 9 | 10 | # This comment is a very long line, which causes problems for people 11 | # who program Python on punch cards. 12 | 13 | parser_errors = [ 14 | (__name__, 1, 1, 'message'), 15 | ] 16 | 17 | errors = [ 18 | 'Parse error at %s:%s "%s" (%s)' % 19 | (filename, num, line, err) 20 | for filename, num, line, err in parser_errors 21 | ] 22 | 23 | 24 | # This is something you might do in an __init__.py 25 | from .submodule import public_function # noqa 26 | 27 | 28 | def f(*args): 29 | print(args) 30 | 31 | f(1, 2, 3, 32 | 4, 5, 6) 33 | -------------------------------------------------------------------------------- /2013-10/flake8/source/commits-per-month.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-10/flake8/source/commits-per-month.png -------------------------------------------------------------------------------- /2013-10/flake8/source/contributors-per-month.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-10/flake8/source/contributors-per-month.png -------------------------------------------------------------------------------- /2013-10/flake8/source/lines-of-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2013-10/flake8/source/lines-of-code.png -------------------------------------------------------------------------------- /2013-10/flake8/source/messy.py: -------------------------------------------------------------------------------- 1 | MAPPING = { 2 | 'a':'A', 3 | } 4 | 5 | LIST = [ 6 | 'a', 7 | 'b', 8 | ] 9 | 10 | # This comment is a very long line, which causes problems for people who program Python on punch cards. 11 | 12 | parser_errors = [ 13 | (__name__, 1, 1, 'message'), 14 | ] 15 | 16 | errors = ['Parse error at %s:%s "%s" (%s)' % (filename, num, line, err) for filename, num, line, err in parser_errors] 17 | 18 | 19 | # This is something you might do in an __init__.py 20 | from .submodule import public_function 21 | 22 | def f(*args): 23 | print(args) 24 | 25 | f(1, 2, 3, 26 | 4, 5, 6) 27 | -------------------------------------------------------------------------------- /2013-10/objects/15-puzzle_old_version.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """15-puzzle 4 | 5 | Simple 15 puzzle game. 6 | """ 7 | 8 | import random 9 | 10 | 11 | class Puzzle(object): 12 | """Represents a "15-puzzle" 13 | 14 | ``size`` is the width and height of the puzzle, supporting puzzles 15 | with dimensions other than 4x4 16 | 17 | >>> print(Puzzle()) 18 | 1 2 3 4 19 | 5 6 7 8 20 | 9 10 11 12 21 | 13 14 15 _ 22 | 23 | >>> print(Puzzle(2)) 24 | 1 2 25 | 3 _ 26 | """ 27 | def __init__(self, size=4): 28 | self._size = size 29 | 30 | self._puzzle = [] 31 | for row in range(self._size): 32 | row_start = row * self._size 33 | self._puzzle.append(map(str, range(row_start + 1, 34 | row_start + 1 + self._size))) 35 | 36 | self._puzzle[-1][-1] = "_" 37 | 38 | def __str__(self): 39 | return "\n".join(["\t".join(row) for row in self._puzzle]) 40 | 41 | def move(self, row, column): 42 | """Move by sliding from the given square toward the empty space. 43 | 44 | >>> print(Puzzle(2).move(1,0)) 45 | 1 2 46 | _ 3 47 | 48 | >>> print(Puzzle(2).move(0,1)) 49 | 1 _ 50 | 3 2 51 | 52 | >>> print(Puzzle(2).move(0,1).move(0,0).move(1,0)) 53 | 3 1 54 | _ 2 55 | 56 | >>> print(Puzzle(2).move(0,0)) 57 | Traceback (most recent call last): 58 | ... 59 | ValueError: locked square at (0, 0) 60 | """ 61 | if "_" in self._puzzle[row]: 62 | return self._move_on_row(row, column) 63 | 64 | # transpose rows and columns 65 | puzzle = map(list, zip(*self._puzzle)) 66 | 67 | if "_" in puzzle[column]: 68 | self._puzzle = puzzle 69 | self._move_on_row(column, row) 70 | self._puzzle = map(list, zip(*self._puzzle)) 71 | else: 72 | raise ValueError("locked square at " + str( (row, column) )) 73 | 74 | return self 75 | 76 | def _move_on_row(self, row, column): 77 | """Move row-wise by sliding the given square toward the empty space. 78 | 79 | >>> print(Puzzle(2)._move_on_row(1,0)) 80 | 1 2 81 | _ 3 82 | 83 | >>> print(Puzzle(3)._move_on_row(2,0)._move_on_row(2,1)) 84 | 1 2 3 85 | 4 5 6 86 | 7 _ 8 87 | 88 | >>> print(Puzzle(2)._move_on_row(0,0)) 89 | Traceback (most recent call last): 90 | … 91 | ValueError: list.remove(x): x not in list 92 | """ 93 | self._puzzle[row].remove("_") 94 | self._puzzle[row].insert(column, "_") 95 | 96 | return self 97 | 98 | def valid_moves(self): 99 | """List the currently valid moves. 100 | 101 | >>> sorted(Puzzle(2).valid_moves()) == [(0, 1), (1, 0)] 102 | True 103 | 104 | >>> sorted(Puzzle(3).valid_moves()) == [(0, 2), (1, 2), \ 105 | (2, 0), (2, 1)] 106 | True 107 | 108 | >>> sorted(Puzzle(3).move(1,2).move(1,1).valid_moves()) == \ 109 | [(0, 1), (1, 0), \ 110 | (1, 2), (2, 1)] 111 | True 112 | """ 113 | for index, row in enumerate(self._puzzle): 114 | if "_" in row: 115 | _column_index = row.index("_") 116 | _row_index = index 117 | break 118 | 119 | row_moves = [(_row_index, column) 120 | for column in range(self._size) 121 | if self._puzzle[_row_index][column] != "_"] 122 | 123 | column_moves = [(row, _column_index) 124 | for row in range(self._size) 125 | if row != _row_index] 126 | 127 | return row_moves + column_moves 128 | 129 | def shuffle(self, iterations=100): 130 | """Shuffle the puzzle""" 131 | 132 | for iteration in xrange(iterations): 133 | move = random.choice(self.valid_moves()) 134 | self.move(*move) 135 | 136 | return self 137 | 138 | def is_solved(self): 139 | """Is the puzzle solved? 140 | 141 | >>> Puzzle().is_solved() 142 | True 143 | 144 | >>> Puzzle().shuffle(1).is_solved() 145 | False 146 | 147 | >>> Puzzle(5).is_solved() 148 | True 149 | 150 | >>> Puzzle(3).move(0,2).is_solved() 151 | False 152 | 153 | """ 154 | 155 | return self._puzzle == Puzzle(self._size)._puzzle 156 | 157 | # ==================================================================== 158 | # doctest test harness 159 | 160 | def _test(_verbose=False): 161 | import doctest 162 | doctest.testmod(verbose=_verbose, optionflags=(doctest.ELLIPSIS | 163 | #doctest.REPORT_NDIFF | 164 | doctest.NORMALIZE_WHITESPACE)) 165 | 166 | # ==================================================================== 167 | # test stub: run doctests if this file is run directly 168 | 169 | if __name__ == "__main__": 170 | _test() 171 | 172 | for size in [2, 3, 3, 4, 5, 10]: 173 | puzzle = Puzzle(size) 174 | puzzle.shuffle(1000 * size) 175 | print(puzzle) 176 | print("") 177 | -------------------------------------------------------------------------------- /2013-10/objects/README.rst: -------------------------------------------------------------------------------- 1 | Musing about Objects 2 | ==================== 3 | 4 | Support code for my `Musing about Objects`_ October PyAtl talk. 5 | 6 | ``15-puzzle_old_version.py`` is the version I wrote as an assignment in 2009. 7 | It uses ``doctest`` as the testing framework; running the file will invoke the 8 | tests. 9 | 10 | The ``fifteen_puzzle`` package contains the newer implementation; its test 11 | suite is in ``tests``. You'll need ``pytest`` to run them:: 12 | 13 | py.test tests 14 | 15 | ``NetworkX_sandbox.ipynb`` is an IPython notebook of my explorations of 16 | `NetworkX`_, which provided the graph data structure for the newer version. 17 | 18 | .. _Musing about Objects: http://pyatl.github.io/talks/2013-10_objects/ 19 | .. _NetworkX: http://networkx.github.io/ 20 | -------------------------------------------------------------------------------- /2013-10/objects/fifteen_puzzle/__init__.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | 3 | 4 | class Puzzle(object): 5 | """Represents a 15-puzzle 6 | 7 | ``size`` is the width and height of the puzzle, supporting puzzles with 8 | dimensions other than 4x4. 9 | 10 | """ 11 | 12 | def __init__(self, size=4): 13 | self.size = size 14 | self._puzzle = nx.grid_2d_graph(size, size) 15 | 16 | # Label each node with its tile value, e.g. (0, 0) is tile '1' in the 17 | # upper left. add_node is a NetworkX graph method to update an 18 | # existing node, in this case by setting its 'value' 19 | for value, coordinates in enumerate(sorted(self._puzzle), 1): 20 | self._puzzle.add_node(coordinates, value=str(value)) 21 | 22 | # Label the empty tile 23 | self._puzzle.add_node((size-1, size-1), value='_') 24 | 25 | @property 26 | def is_solved(self): 27 | """Indicate whether the puzzle is in a solved state""" 28 | 29 | # TODO: implementation left as an exercise 30 | return True 31 | 32 | def coordinates_for(self, value): 33 | """Determine the coordinates for the specified number value 34 | 35 | Returns a 2-tuple representing the coordinates for the given value, 36 | with (0, 0) representing the top left corner of the puzzle. 37 | 38 | """ 39 | 40 | for node in self._puzzle: 41 | if self._puzzle.node[node]['value'] == str(value): 42 | return node 43 | 44 | def move(self, value): 45 | """Move a tile by sliding it to the empty square. 46 | 47 | Raises ValueError if the requested tile is locked in place. 48 | 49 | """ 50 | 51 | empty = self.coordinates_for('_') 52 | origin = self.coordinates_for(value) 53 | 54 | if empty not in self._puzzle.neighbors(origin): 55 | raise ValueError('locked square at ' + str(origin)) 56 | 57 | # "move" the tile by swapping its value with the empty tiles 58 | self._puzzle.add_node(empty, value=str(value)) 59 | self._puzzle.add_node(origin, value='_') 60 | 61 | @property 62 | def valid_moves(self): 63 | """List of currently valid moves, returned as tile labels, e.g. 64 | 65 | ['15', '12'] 66 | 67 | """ 68 | 69 | empty = self.coordinates_for('_') 70 | 71 | return [self._puzzle.node[node]['value'] 72 | for node in self._puzzle.neighbors(empty)] 73 | 74 | # TODO: __str__ and shuffle left as an exercise 75 | -------------------------------------------------------------------------------- /2013-10/objects/setup.cfg: -------------------------------------------------------------------------------- 1 | [egg_info] 2 | tag_build = dev 3 | tag_svn_revision = true 4 | -------------------------------------------------------------------------------- /2013-10/objects/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import sys, os 3 | 4 | version = '0.0' 5 | 6 | setup(name='fifteen_puzzle', 7 | version=version, 8 | description="", 9 | long_description="""\ 10 | """, 11 | classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers 12 | keywords='', 13 | author='', 14 | author_email='', 15 | url='', 16 | license='', 17 | packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), 18 | include_package_data=True, 19 | zip_safe=False, 20 | install_requires=[ 21 | # -*- Extra requirements: -*- 22 | ], 23 | entry_points=""" 24 | # -*- Entry points: -*- 25 | """, 26 | ) 27 | -------------------------------------------------------------------------------- /2013-10/objects/tests/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /2013-10/objects/tests/conftest.py: -------------------------------------------------------------------------------- 1 | from fifteen_puzzle import Puzzle 2 | 3 | import pytest 4 | 5 | 6 | @pytest.fixture 7 | def puzzle(): 8 | """Fixture providing a new Puzzle of the default size (4x4)""" 9 | 10 | return Puzzle() 11 | -------------------------------------------------------------------------------- /2013-10/objects/tests/test_when_calculating_valid_moves.py: -------------------------------------------------------------------------------- 1 | def test_initial_valid_moves_should_be_twelve_and_fifteen(puzzle): 2 | assert ['12', '15'] == sorted(puzzle.valid_moves, key=int) 3 | 4 | 5 | def test_valid_after_horizontal_move(puzzle): 6 | puzzle.move(15) 7 | 8 | assert ['11', '14', '15'] == sorted(puzzle.valid_moves, key=int) 9 | 10 | 11 | def test_valid_after_vertical_move(puzzle): 12 | puzzle.move(12) 13 | 14 | assert ['8', '11', '12'] == sorted(puzzle.valid_moves, key=int) 15 | 16 | 17 | def test_valid_after_move_sequence(puzzle): 18 | puzzle.move(12) 19 | puzzle.move(11) 20 | puzzle.move(10) 21 | puzzle.move(6) 22 | 23 | assert ['2', '5', '6', '7'] == sorted(puzzle.valid_moves, key=int) 24 | -------------------------------------------------------------------------------- /2013-10/objects/tests/test_when_create_new_puzzle.py: -------------------------------------------------------------------------------- 1 | from fifteen_puzzle import Puzzle 2 | 3 | import pytest 4 | 5 | 6 | def test_should_succeed(): 7 | Puzzle() 8 | 9 | 10 | def test_default_size_should_be_4(puzzle): 11 | assert 4 == puzzle.size 12 | 13 | 14 | @pytest.mark.parametrize('size', (2, 3, 5)) 15 | def test_can_set_puzzle_size(size): 16 | assert size == Puzzle(size).size 17 | 18 | 19 | def test_new_puzzle_should_be_solved(puzzle): 20 | assert puzzle.is_solved 21 | 22 | 23 | @pytest.mark.parametrize( 24 | ('location', 'value'), 25 | [ 26 | ((0, 0), '1'), 27 | ((0, 1), '2'), 28 | ((0, 2), '3'), 29 | ((0, 3), '4'), 30 | ((1, 0), '5'), 31 | ((1, 1), '6'), 32 | ((1, 2), '7'), 33 | ((1, 3), '8'), 34 | ((2, 0), '9'), 35 | ((2, 1), '10'), 36 | ((2, 2), '11'), 37 | ((2, 3), '12'), 38 | ((3, 0), '13'), 39 | ((3, 1), '14'), 40 | ((3, 2), '15'), 41 | ] 42 | ) 43 | def test_new_puzzle_should_be_in_order(puzzle, location, value): 44 | assert location == puzzle.coordinates_for(value) 45 | 46 | 47 | def test_new_puzzle_empty_should_be_bottom_right(puzzle): 48 | assert (3, 3) == puzzle.coordinates_for('_') 49 | -------------------------------------------------------------------------------- /2013-10/objects/tests/test_when_moving.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | def test_valid_horizontal_move_should_succeed(puzzle): 5 | puzzle.move(15) 6 | 7 | 8 | def test_valid_horizontal_move_should_update_tile_coordinates(puzzle): 9 | old_empty = puzzle.coordinates_for('_') 10 | puzzle.move(15) 11 | 12 | assert old_empty == puzzle.coordinates_for(15) 13 | 14 | 15 | def test_valid_horizontal_move_should_update_empty_tile_coordinates(puzzle): 16 | origin = puzzle.coordinates_for(15) 17 | puzzle.move(15) 18 | 19 | assert origin == puzzle.coordinates_for('_') 20 | 21 | 22 | def test_valid_vertical_move_should_succeed(puzzle): 23 | puzzle.move(12) 24 | 25 | 26 | def test_valid_vertical_move_should_update_tile_coordinates(puzzle): 27 | old_empty = puzzle.coordinates_for('_') 28 | puzzle.move(12) 29 | 30 | assert old_empty == puzzle.coordinates_for(12) 31 | 32 | 33 | def test_valid_vertical_move_should_update_empty_tile_coordinates(puzzle): 34 | origin = puzzle.coordinates_for(12) 35 | puzzle.move(12) 36 | 37 | assert origin == puzzle.coordinates_for('_') 38 | 39 | 40 | @pytest.mark.parametrize('invalid', (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14)) 41 | def test_invalid_move_should_raise(puzzle, invalid): 42 | with pytest.raises(ValueError): 43 | puzzle.move(invalid) 44 | 45 | 46 | @pytest.mark.parametrize( 47 | ('first', 'second'), 48 | [ 49 | (15, 15), 50 | (15, 14), 51 | (15, 11), 52 | (12, 12), 53 | (12, 11), 54 | (12, 8), 55 | ] 56 | ) 57 | def test_move_sequence_should_succeed(puzzle, first, second): 58 | puzzle.move(first) 59 | puzzle.move(second) 60 | 61 | 62 | @pytest.mark.parametrize( 63 | ('first', 'invalid'), 64 | [ 65 | (15, 13), 66 | (15, 10), 67 | (15, 12), 68 | (12, 15), 69 | (12, 10), 70 | (12, 4), 71 | ] 72 | ) 73 | def test_move_sequence_with_second_invalid_should_fail(puzzle, first, invalid): 74 | puzzle.move(first) 75 | 76 | with pytest.raises(ValueError): 77 | puzzle.move(invalid) 78 | -------------------------------------------------------------------------------- /2014-08/micropython/README.md: -------------------------------------------------------------------------------- 1 | Introduction to MicroPython 2 | ============================ 3 | JR Rickerson 4 | 5 | Overview 6 | -------- 7 | MicroPython is a reimplementation of the Python 3 language for micrcontrollers. 8 | The Kickstarter project provided a copy of the MicroPython code embedded on a 9 | a small microcontroller board called the "pyboard," and a Python module to 10 | provide access to the components on the board. 11 | 12 | Slides 13 | ------ 14 | Slides are available via Google Docs: 15 | https://docs.google.com/presentation/d/1Y3Wslr_so9h0rhLIcHzoOU1z7XNXuJMO1-rfwCNNMuM/edit?usp=sharing 16 | 17 | Code 18 | ---- 19 | Demo scripts presented during the talk can be found on Github: 20 | https://github.com/pyatl/talks/tree/master/2014-08/micropython/demo 21 | 22 | Resources 23 | --------- 24 | Resources for learning more: 25 | - MicroPython website (info, docs, diagrams, etc): http://www.micropython.org 26 | - AdaFruit (electronics kits, books, parts): http://www.adafruit.com 27 | - SparkFun (electronics kits, books, parts, projects): http://www.sparkfun.com 28 | - Microcenter (local electronics store with hobby section): http://www.microcenter.com 29 | 30 | -------------------------------------------------------------------------------- /2014-08/micropython/demo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2014-08/micropython/demo/__init__.py -------------------------------------------------------------------------------- /2014-08/micropython/demo/accelerometer.py: -------------------------------------------------------------------------------- 1 | """ Interact with the accelerometer. Light up different leds 2 | when different axes are not level, within a tolerance. """ 3 | import pyb 4 | 5 | ledx = pyb.LED(1) 6 | ledy = pyb.LED(2) 7 | ledz = pyb.LED(3) 8 | accel = pyb.Accel() 9 | SENSITIVITY = 5 10 | 11 | def run(): 12 | while True: 13 | x = accel.x() 14 | y = accel.y() 15 | z = accel.z() 16 | if abs(x) > SENSITIVITY: 17 | ledx.on() 18 | else: 19 | ledx.off() 20 | if abs(y) > SENSITIVITY: 21 | ledy.on() 22 | else: 23 | ledy.off() 24 | if abs(z) > SENSITIVITY: 25 | ledz.on() 26 | else: 27 | ledz.off() 28 | pyb.delay(200) 29 | 30 | -------------------------------------------------------------------------------- /2014-08/micropython/demo/hello.py: -------------------------------------------------------------------------------- 1 | """ Hello World on the pyboard. Just turn on the blue LED """ 2 | import pyb 3 | 4 | led_blue = pyb.LED(4) 5 | led_blue.on() 6 | -------------------------------------------------------------------------------- /2014-08/micropython/demo/leds.py: -------------------------------------------------------------------------------- 1 | """ Make your pyboard into a disco! Alternate leds in a cycle """ 2 | import pyb 3 | 4 | led_list = [pyb.LED(i) for i in range(1, 5)] 5 | 6 | def run(): 7 | i = 0 8 | while True: 9 | led_list[i].off() 10 | i += 1 11 | if i >= len(led_list): 12 | i = 0 13 | led_list[i].on() 14 | pyb.delay(200) 15 | 16 | -------------------------------------------------------------------------------- /2014-08/micropython/demo/loop.py: -------------------------------------------------------------------------------- 1 | """ Looping your program - Make the red LED blink on and off every 500 ms """ 2 | import pyb 3 | 4 | led_red = pyb.LED(1) 5 | 6 | def run(): 7 | while True: 8 | led_red.toggle() 9 | pyb.delay(500) 10 | 11 | -------------------------------------------------------------------------------- /2014-08/micropython/demo/switch.py: -------------------------------------------------------------------------------- 1 | """ Use the USR switch on the pyboard to toggle the blue led on and off """ 2 | import pyb 3 | 4 | led = pyb.LED(4) 5 | 6 | def run(): 7 | switch = pyb.Switch() 8 | switch.callback(lambda: led.toggle()) 9 | -------------------------------------------------------------------------------- /2014-09/roman/README.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | Party Like it's 500BC 3 | ===================== 4 | 5 | This talk from the September 2014 meeting of PyAtl examined the thought 6 | process developers use to solve problems, including 7 | 8 | - decomposition of a problem specification 9 | - considering tradeoffs among design choices 10 | - iterative refinement to build a solution 11 | 12 | We also looked at the specific details of implementing a solution in 13 | Python. 14 | 15 | Slides: http://pyatl.github.io/talks/2014-09_roman/500BC.html 16 | 17 | 18 | doctest 19 | ======= 20 | 21 | The slides include a full ``doctest`` suite. To run it, download the 22 | `source for the slides`_, then run:: 23 | 24 | python -m doctest -v 500BC.rst 25 | 26 | Try playing around with the source: start by breaking something to see 27 | the tests fail, then tackle the challenge problem! 28 | 29 | 30 | .. _`source for the slides`: https://raw.githubusercontent.com/pyatl/talks/master/2014-09/roman/500BC.rst 31 | -------------------------------------------------------------------------------- /2014-09/stdlib-tour/requirements.txt: -------------------------------------------------------------------------------- 1 | Sphinx 2 | hieroglyph 3 | flake8 -------------------------------------------------------------------------------- /2014-09/stdlib-tour/source/_static/custom.css: -------------------------------------------------------------------------------- 1 | .slides > article { 2 | /* background-color: lightblue; */ 3 | color: #222222; 4 | } 5 | 6 | h2 { 7 | bottom: 50px; 8 | } 9 | 10 | /* Use when the pre tag contents do not fit on the screen otherwise. */ 11 | .small { 12 | font-size: 70%; 13 | } 14 | 15 | /* Center block quotes vertically and horizontally. Based on 16 | http://css-tricks.com/vertically-center-multi-lined-text/ 17 | */ 18 | 19 | blockquote { 20 | font-size: 130%; 21 | display: table; 22 | vertical-align: middle; 23 | text-align: center; 24 | height: 80%; 25 | width: 80%; 26 | } 27 | 28 | blockquote div { 29 | display: table-cell; 30 | vertical-align: middle; 31 | text-align: center; 32 | } 33 | 34 | table.highlighttable td { 35 | border: 0; 36 | padding: 0; 37 | } -------------------------------------------------------------------------------- /2014-09/stdlib-tour/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # build configuration file, created by sphinx-quickstart 4 | # on 2013-09-08 # 11:34:00 dhellmann 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import datetime 16 | # import sys 17 | # import os 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | #sys.path.insert(0, os.path.abspath('.')) 23 | 24 | # -- General configuration --------------------------------------------------- 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | #needs_sphinx = '1.0' 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = [ 33 | 'sphinx.ext.doctest', 34 | 'hieroglyph', 35 | ] 36 | 37 | slide_theme_options = { 38 | 'custom_css': 'custom.css', 39 | } 40 | 41 | slide_numbers = True 42 | 43 | # Add any paths that contain templates here, relative to this directory. 44 | templates_path = ['_templates'] 45 | 46 | # The suffix of source filenames. 47 | source_suffix = '.rst' 48 | 49 | # The encoding of source files. 50 | #source_encoding = 'utf-8-sig' 51 | 52 | # The master toctree document. 53 | master_doc = 'index' 54 | 55 | # General information about the project. 56 | project = u'talk' 57 | today = datetime.datetime.today() 58 | copyright = u'%s, Doug Hellmann' % today.year 59 | 60 | # The version info for the project you're documenting, acts as replacement for 61 | # |version| and |release|, also used in various other places throughout the 62 | # built documents. 63 | # 64 | # The short X.Y version. 65 | version = '1.0' 66 | # The full version, including alpha/beta/rc tags. 67 | release = version 68 | 69 | # The language for content autogenerated by Sphinx. Refer to documentation 70 | # for a list of supported languages. 71 | #language = None 72 | 73 | # There are two options for replacing |today|: either, you set today to some 74 | # non-false value, then it is used: 75 | #today = '' 76 | # Else, today_fmt is used as the format for a strftime call. 77 | #today_fmt = '%B %d, %Y' 78 | 79 | # List of patterns, relative to source directory, that match files and 80 | # directories to ignore when looking for source files. 81 | exclude_patterns = [] 82 | 83 | # The reST default role (used for this markup: `text`) to use for all 84 | # documents. 85 | #default_role = None 86 | 87 | # If true, '()' will be appended to :func: etc. cross-reference text. 88 | #add_function_parentheses = True 89 | 90 | # If true, the current module name will be prepended to all description 91 | # unit titles (such as .. function::). 92 | #add_module_names = True 93 | 94 | # If true, sectionauthor and moduleauthor directives will be shown in the 95 | # output. They are ignored by default. 96 | #show_authors = False 97 | 98 | # The name of the Pygments (syntax highlighting) style to use. 99 | pygments_style = 'sphinx' 100 | 101 | # A list of ignored prefixes for module index sorting. 102 | #modindex_common_prefix = [] 103 | 104 | # If true, keep warnings as "system message" paragraphs in the built documents. 105 | #keep_warnings = False 106 | 107 | 108 | # -- Options for HTML output ------------------------------------------------- 109 | 110 | # The theme to use for HTML and HTML Help pages. See the documentation for 111 | # a list of builtin themes. 112 | html_theme = 'default' 113 | 114 | # Theme options are theme-specific and customize the look and feel of a theme 115 | # further. For a list of options available for each theme, see the 116 | # documentation. 117 | #html_theme_options = {} 118 | 119 | # Add any paths that contain custom themes here, relative to this directory. 120 | #html_theme_path = [] 121 | 122 | # The name for this set of Sphinx documents. If None, it defaults to 123 | # " v documentation". 124 | #html_title = None 125 | 126 | # A shorter title for the navigation bar. Default is the same as html_title. 127 | #html_short_title = None 128 | 129 | # The name of an image file (relative to this directory) to place at the top 130 | # of the sidebar. 131 | #html_logo = None 132 | 133 | # The name of an image file (within the static path) to use as favicon of the 134 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 135 | # pixels large. 136 | #html_favicon = None 137 | 138 | # Add any paths that contain custom static files (such as style sheets) here, 139 | # relative to this directory. They are copied after the builtin static files, 140 | # so a file named "default.css" will overwrite the builtin "default.css". 141 | html_static_path = ['_static'] 142 | 143 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 144 | # using the given strftime format. 145 | #html_last_updated_fmt = '%b %d, %Y' 146 | 147 | # If true, SmartyPants will be used to convert quotes and dashes to 148 | # typographically correct entities. 149 | #html_use_smartypants = True 150 | 151 | # Custom sidebar templates, maps document names to template names. 152 | #html_sidebars = {} 153 | 154 | # Additional templates that should be rendered to pages, maps page names to 155 | # template names. 156 | #html_additional_pages = {} 157 | 158 | # If false, no module index is generated. 159 | #html_domain_indices = True 160 | 161 | # If false, no index is generated. 162 | #html_use_index = True 163 | 164 | # If true, the index is split into individual pages for each letter. 165 | #html_split_index = False 166 | 167 | # If true, links to the reST sources are added to the pages. 168 | #html_show_sourcelink = True 169 | 170 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 171 | #html_show_sphinx = True 172 | 173 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 174 | #html_show_copyright = True 175 | 176 | # If true, an OpenSearch description file will be output, and all pages will 177 | # contain a tag referring to it. The value of this option must be the 178 | # base URL from which the finished HTML is served. 179 | #html_use_opensearch = '' 180 | 181 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 182 | #html_file_suffix = None -------------------------------------------------------------------------------- /2014-09/stdlib-tour/source/dis_simple.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | 4 | my_dict = { 'a':1 } 5 | -------------------------------------------------------------------------------- /2014-09/stdlib-tour/source/index.rst: -------------------------------------------------------------------------------- 1 | ========================================== 2 | A Whirlwind Tour of the Standard Library 3 | ========================================== 4 | 5 | | Doug Hellmann 6 | | doug@doughellmann.com 7 | | @doughellmann 8 | | PyATL 9 | | 2014-09 10 | 11 | ``string``: Simple Templates 12 | ============================ 13 | 14 | .. literalinclude:: string_template.py 15 | 16 | :: 17 | 18 | TEMPLATE: 19 | foo 20 | $ 21 | fooiable 22 | 23 | .. note:: 24 | 25 | Also includes ASCII constants. 26 | 27 | ``textwrap``: Formatting 28 | ======================== 29 | 30 | * fill / wrap / justify 31 | * indent 32 | * dedent 33 | * hanging indent 34 | 35 | 36 | 37 | ``re``: Pattern Matching 38 | ======================== 39 | 40 | * regular expressions 41 | * search 42 | * replace 43 | 44 | Numeric and Mathematical 45 | ======================== 46 | 47 | * decimal – fixed decimal arithmetic 48 | * fractions – rational numbers 49 | * math – specialized mathematical functions 50 | * random – pseudo-random number generators 51 | 52 | ``time`` and ``datetime`` 53 | ========================= 54 | 55 | .. literalinclude:: time_info.py 56 | 57 | :: 58 | 59 | TIME: 1410107908.56 60 | TODAY: 2014-09-07 12:38:28.574200 61 | 62 | ``collections``: Data Structures 63 | ================================ 64 | 65 | * namedtuple 66 | * deque 67 | * Counter 68 | * OrderedDict 69 | * defaultdict 70 | 71 | Data Formats 72 | ============ 73 | 74 | * json – JavaScript Object Notation 75 | * base64 – ASCII encoding of binary data 76 | * csv – Comma Separated Value 77 | * pickle – Python-specific serialization format 78 | 79 | Archive formats 80 | =============== 81 | 82 | * tarfile 83 | * zipfile 84 | 85 | Compression 86 | =========== 87 | 88 | * zlib 89 | * gzip 90 | * bz2 91 | 92 | .. note:: 93 | 94 | These can be combined with the tarfile module, but they can also be 95 | applied to other files and even sockets or other data streams that 96 | are not files. 97 | 98 | Databases 99 | ========= 100 | 101 | * Key/Value stores 102 | 103 | * anydbm 104 | * shelve 105 | 106 | * Relational Database 107 | 108 | * sqlite3 109 | 110 | Operating System Tools 111 | ====================== 112 | 113 | * os – interface to the operating system 114 | * os.path – work with filenames 115 | * sys – interface to the running system 116 | 117 | Application Building Tools 118 | ========================== 119 | 120 | * argparse – command line arguments 121 | * logging – files, remote services, etc. 122 | * getpass – ask for data securely 123 | * readline – interactive prompting 124 | * cmd – interactive command shell framework 125 | * gettext – internationalization and translation 126 | * fileinput – UNIX pipeline tool framework 127 | 128 | Concurrency 129 | =========== 130 | 131 | * threading – single-process concurrent execution 132 | * Queue – FIFO data structure 133 | * multiprocessing – manage multiple processes as threads 134 | 135 | Internet Tools 136 | ============== 137 | 138 | * urllib/urllib2 – retrieve data 139 | * urlparse – work with URLs 140 | * SimpleHTTPServer – static content server 141 | * xmlrpclib – remote function execution over HTTP 142 | * SimpleXMLRPCServer – serve functions over HTTP 143 | 144 | Testing Tools 145 | ============= 146 | 147 | * unittest – test framework 148 | * doctest – tests in documentation 149 | * pdb – debugger 150 | * profile – performance analysis 151 | * trace – watch statements being executed 152 | 153 | ``trace`` 154 | ========= 155 | 156 | .. literalinclude:: trace_main.py 157 | 158 | .. literalinclude:: trace_recurse.py 159 | 160 | ``trace`` 161 | ========= 162 | 163 | :: 164 | 165 | $ python trace_main.py 166 | This is the main program. 167 | recurse(2) 168 | recurse(1) 169 | recurse(0) 170 | 171 | ``trace`` 172 | ========= 173 | 174 | .. rst-class:: small 175 | 176 | :: 177 | 178 | $ python -m trace --trace trace_main.py 179 | --- modulename: trace_main, funcname: 180 | trace_main.py(2): from trace_recurse import recurse 181 | --- modulename: trace_recurse, funcname: 182 | trace_recurse.py(4): def recurse(level): 183 | trace_recurse.py(11): def not_called(): 184 | trace_main.py(5): def main(): 185 | trace_main.py(10): if __name__ == '__main__': 186 | trace_main.py(11): main() 187 | --- modulename: trace_main, funcname: main 188 | trace_main.py(6): print 'This is the main program.' 189 | This is the main program. 190 | trace_main.py(7): recurse(2) 191 | --- modulename: trace_recurse, funcname: recurse 192 | trace_recurse.py(5): print 'recurse(%s)' % level 193 | recurse(2) 194 | trace_recurse.py(6): if level: 195 | trace_recurse.py(7): recurse(level-1) 196 | --- modulename: trace_recurse, funcname: recurse 197 | trace_recurse.py(5): print 'recurse(%s)' % level 198 | recurse(1) 199 | trace_recurse.py(6): if level: 200 | trace_recurse.py(7): recurse(level-1) 201 | 202 | ... 203 | 204 | 205 | Language Tools 206 | ============== 207 | 208 | * gc – garbage collector 209 | * inspect – ask questions about code objects at runtime 210 | * imp – module import system 211 | * dis - show the byte-code for source 212 | 213 | ``dis`` 214 | ======= 215 | 216 | .. literalinclude:: dis_simple.py 217 | :linenos: 218 | 219 | (line num, instruction, opcode, arguments) 220 | 221 | :: 222 | 223 | $ python -m dis dis_simple.py 224 | 4 0 BUILD_MAP 1 225 | 3 LOAD_CONST 0 (1) 226 | 6 LOAD_CONST 1 ('a') 227 | 9 STORE_MAP 228 | 10 STORE_NAME 0 (my_dict) 229 | 13 LOAD_CONST 2 (None) 230 | 16 RETURN_VALUE 231 | 232 | References 233 | ========== 234 | 235 | Internet 236 | 237 | * https://docs.python.org/2.7/library/index.html 238 | * http://pymotw.com 239 | 240 | Books 241 | 242 | * *Python Essential Reference* – Beasley 243 | * *The Python Standard Library by Example* – Hellmann 244 | -------------------------------------------------------------------------------- /2014-09/stdlib-tour/source/string_template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import string 4 | 5 | values = {'var': 'foo'} 6 | 7 | t = string.Template(""" 8 | $var 9 | $$ 10 | ${var}iable 11 | """) 12 | 13 | print 'TEMPLATE:', t.substitute(values) 14 | -------------------------------------------------------------------------------- /2014-09/stdlib-tour/source/time_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import time 4 | import datetime 5 | 6 | 7 | print 'TIME:', time.time() 8 | print 'TODAY:', datetime.datetime.today() 9 | -------------------------------------------------------------------------------- /2014-09/stdlib-tour/source/trace_main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from trace_recurse import recurse 3 | 4 | 5 | def main(): 6 | print 'This is the main program.' 7 | recurse(2) 8 | return 9 | 10 | if __name__ == '__main__': 11 | main() 12 | -------------------------------------------------------------------------------- /2014-09/stdlib-tour/source/trace_recurse.py: -------------------------------------------------------------------------------- 1 | def recurse(level): 2 | print 'recurse(%s)' % level 3 | if level: 4 | recurse(level-1) 5 | return 6 | -------------------------------------------------------------------------------- /2015-03/redacted-tweets/README.md: -------------------------------------------------------------------------------- 1 | # Redacted Tweets 2 | A presentation about a twitter bot that redacts tweets (and makes mad libs!) 3 | 4 | Links: 5 | https://twitter.com/redactedtweet 6 | https://twitter.com/MadTweetLib 7 | 8 | Source: 9 | https://github.com/nloadholtes/Redacted-Tweets 10 | 11 | Presenter: 12 | Nick Loadholtes 13 | https://twitter.com/nloadholtes 14 | https://github.com/nloadholtes 15 | -------------------------------------------------------------------------------- /2015-03/redacted-tweets/redacted_twitter_nick_loadholtes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2015-03/redacted-tweets/redacted_twitter_nick_loadholtes.pdf -------------------------------------------------------------------------------- /2015-06/effective_python/README.md: -------------------------------------------------------------------------------- 1 | # Book review: Effective Python 2 | _____ 3 | A review of the book "Effective Python" by Brett Slatkin 4 | 5 | Book website: 6 | 7 | Code examples: 8 | 9 | Presenter: Nick Loadholtes 10 | -------------------------------------------------------------------------------- /2015-06/effective_python/effective_python_presentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2015-06/effective_python/effective_python_presentation.pdf -------------------------------------------------------------------------------- /2015-09/help-on-irc/README.md: -------------------------------------------------------------------------------- 1 | This Man Needed Help, So He Went on IRC 2 | ======================================= 3 | 4 | You Won't Believe What Happened Next! 5 | ------------------------------------- 6 | 7 | A presentation about using IRC to find help with Python problems. 8 | 9 | Presenter: 10 | Hank Gay 11 | -------------------------------------------------------------------------------- /2015-09/help-on-irc/help-on-irc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2015-09/help-on-irc/help-on-irc.pdf -------------------------------------------------------------------------------- /2017-02/tcp-socket-file-sharing/client: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | nc localhost 8080 > output.jpg 4 | -------------------------------------------------------------------------------- /2017-02/tcp-socket-file-sharing/helpme.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2017-02/tcp-socket-file-sharing/helpme.jpg -------------------------------------------------------------------------------- /2017-02/tcp-socket-file-sharing/serve.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | import socket 3 | 4 | image = open('helpme.jpg', 'rb') 5 | 6 | sock = socket.socket() 7 | # When you set a socket option, you set its name and level 8 | # The level argument (SOL_SOCKET) specifies the protocol level at which the 9 | # option (SO_REUSEADDR) resides 10 | # 11 | # SO_REUSEADDR tells the kernel that even if this port is busy (in 12 | # the TIME_WAIT state), go ahead and reuse it anyway 13 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 14 | sock.bind(('0.0.0.0', 8080)) 15 | print('Listening on 0.0.0.0:8080') 16 | 17 | sock.listen(1) # number of queued connections 18 | 19 | client, addr = sock.accept() 20 | print('Got connection from', addr) 21 | client.sendfile(image) 22 | for x in [client, sock, image]: 23 | x.close() 24 | -------------------------------------------------------------------------------- /2017-03-09/python-packaging/README.md: -------------------------------------------------------------------------------- 1 | Included are the raw Jupyter Notebook slides and the built slides. 2 | 3 | To build the slides, first install jupyter: 4 | 5 | pip install --user jupyter 6 | 7 | Remember, don't run with escalated privileges (admin, sudo, etc) 8 | 9 | Then run: 10 | 11 | jupyter nbconvert --to slides --post serve python_packaging.ipynb 12 | -------------------------------------------------------------------------------- /2017-10-12/python-profiling/1.filesize.py: -------------------------------------------------------------------------------- 1 | # python 1.filesize.py 2 | 3 | import cStringIO 4 | from collections import OrderedDict 5 | import pexpect 6 | 7 | PROMPTS = OrderedDict({ 8 | pexpect.EOF: None, 9 | }) 10 | 11 | child = pexpect.spawn('du', ['-h', __file__]) 12 | child.logfile_read = cStringIO.StringIO() 13 | while child.isalive(): 14 | index = child.expect(PROMPTS.keys()) 15 | answer = PROMPTS.values()[index] 16 | if answer is not None: 17 | child.sendline(answer) 18 | 19 | print child.logfile_read.getvalue() 20 | -------------------------------------------------------------------------------- /2017-10-12/python-profiling/2.filesize.py: -------------------------------------------------------------------------------- 1 | # python 2.filesize.py 2 | 3 | import cStringIO 4 | from collections import OrderedDict 5 | import pexpect 6 | import re 7 | 8 | PROMPTS = OrderedDict({ 9 | pexpect.EOF: None, 10 | re.compile(r'Enter passphrase for .*:\s*?$', re.M): 'some-ssh-passphrase' 11 | }) 12 | 13 | child = pexpect.spawn('du', ['-h', __file__]) 14 | child.logfile_read = cStringIO.StringIO() 15 | while child.isalive(): 16 | index = child.expect(PROMPTS.keys()) 17 | answer = PROMPTS.values()[index] 18 | if answer is not None: 19 | child.sendline(answer) 20 | 21 | print child.logfile_read.getvalue() 22 | -------------------------------------------------------------------------------- /2017-10-12/python-profiling/3.display_file.py: -------------------------------------------------------------------------------- 1 | # python -m cProfile -s cumulative 3.display_file.py | less 2 | 3 | import cStringIO 4 | from collections import OrderedDict 5 | import pexpect 6 | 7 | PROMPTS = OrderedDict({ 8 | pexpect.EOF: None, 9 | }) 10 | 11 | child = pexpect.spawn('cat', ['large']) 12 | child.logfile_read = cStringIO.StringIO() 13 | while child.isalive(): 14 | index = child.expect(PROMPTS.keys()) 15 | answer = PROMPTS.values()[index] 16 | if answer is not None: 17 | child.sendline(answer) 18 | -------------------------------------------------------------------------------- /2017-10-12/python-profiling/4.pstats.py: -------------------------------------------------------------------------------- 1 | # python 4.pstats.py 2 | # pip install gprof2dot 3 | # brew/yum/apt install graphviz 4 | # gprof2dot -f pstats output.pstats | dot -Tpng -o output.png 5 | 6 | from profiler import profile 7 | 8 | import cStringIO 9 | from collections import OrderedDict 10 | import pexpect 11 | 12 | PROMPTS = OrderedDict({ 13 | pexpect.EOF: None, 14 | }) 15 | 16 | with profile(): 17 | child = pexpect.spawn('cat', ['large']) 18 | child.logfile_read = cStringIO.StringIO() 19 | while child.isalive(): 20 | index = child.expect(PROMPTS.keys()) 21 | answer = PROMPTS.values()[index] 22 | if answer is not None: 23 | child.sendline(answer) 24 | -------------------------------------------------------------------------------- /2017-10-12/python-profiling/5.snakeviz.py: -------------------------------------------------------------------------------- 1 | # python 4.pstats.py 2 | # pip install snakeviz 3 | # snakeviz output.pstats 4 | -------------------------------------------------------------------------------- /2017-10-12/python-profiling/6.line_profiling.py: -------------------------------------------------------------------------------- 1 | # pprofile 3.display_file.py | less 2 | -------------------------------------------------------------------------------- /2017-10-12/python-profiling/profiler.py: -------------------------------------------------------------------------------- 1 | import cProfile 2 | import pstats 3 | from contextlib import contextmanager 4 | 5 | @contextmanager 6 | def profile(): 7 | prof = cProfile.Profile() 8 | prof.enable() 9 | yield 10 | prof.disable() 11 | with open('output.pstats', 'w') as f: 12 | pstats.Stats(prof).dump_stats(f.name) 13 | -------------------------------------------------------------------------------- /2019-05-09/python_in_security/CandidPythonAtlanta20190509-Final.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyatl/talks/f709dfe4bbfa25a5bd646cdf57003349cbc97f91/2019-05-09/python_in_security/CandidPythonAtlanta20190509-Final.pptx -------------------------------------------------------------------------------- /2019-05-09/python_in_security/README.md: -------------------------------------------------------------------------------- 1 | # Python in Security 2 | ## Jonathan Glass, Candid Partners 3 | -------------------------------------------------------------------------------- /2019-05-09/responder-tour/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 JR Rickerson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /2019-05-09/responder-tour/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | responder = "*" 8 | requests = "*" 9 | 10 | [dev-packages] 11 | 12 | [requires] 13 | python_version = "3.7" 14 | -------------------------------------------------------------------------------- /2019-05-09/responder-tour/README.md: -------------------------------------------------------------------------------- 1 | # Quick Tour of Responder 2 | ## JR Rickerson 3 | 4 | Github repository: https://github.com/jrrickerson/responder-tour 5 | -------------------------------------------------------------------------------- /2019-05-09/responder-tour/client.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | data = {'stuff': 'things'} 4 | r = requests.post('http://127.0.0.1:5042/incoming', data=data) 5 | 6 | print(r.text) 7 | 8 | -------------------------------------------------------------------------------- /2019-05-09/responder-tour/features.py: -------------------------------------------------------------------------------- 1 | import time 2 | import responder 3 | 4 | api = responder.API() 5 | 6 | 7 | # Returning JSON? Just return a dict! 8 | @api.route("/hello/{who}/json") 9 | def hello_to(req, resp, *, who): 10 | resp.media = {"hello": who} 11 | 12 | 13 | @api.route("/hello/{who}/html") 14 | def hello_html(req, resp, *, who): 15 | resp.html = api.template('template.html', who=who) 16 | 17 | 18 | @api.route("/teapot") 19 | def teapot(req, resp): 20 | resp.status_code = api.status_codes.HTTP_416 # ...or 416 21 | 22 | 23 | @api.route("/pizza") 24 | def pizza_pizza(req, resp): 25 | resp.headers['X-Pizza'] = '42' 26 | 27 | 28 | @api.route("/incoming") 29 | async def receive_incoming(req, resp): 30 | 31 | @api.background.task 32 | def process_data(data): 33 | """Just sleeps for three seconds, as a demo.""" 34 | print('Doing background stuff') 35 | time.sleep(1) 36 | print('Doing more background stuff...') 37 | time.sleep(1) 38 | print('Almost done...') 39 | time.sleep(1) 40 | print('Done!') 41 | 42 | # Parse the incoming data as form-encoded. 43 | # Note: 'json' and 'yaml' formats are also automatically supported. 44 | data = await req.media() 45 | 46 | # Process the data (in the background). 47 | process_data(data) 48 | 49 | # Immediately respond that upload was successful. 50 | resp.media = {'success': True} 51 | 52 | 53 | if __name__ == '__main__': 54 | api.run() 55 | -------------------------------------------------------------------------------- /2019-05-09/responder-tour/greeting.py: -------------------------------------------------------------------------------- 1 | import responder 2 | 3 | api = responder.API() 4 | 5 | 6 | @api.route("/{greeting}") 7 | async def greet_world(req, resp, *, greeting): 8 | resp.text = f"{greeting}, world!" 9 | 10 | if __name__ == '__main__': 11 | api.run() 12 | -------------------------------------------------------------------------------- /2019-05-09/responder-tour/templates/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Look, {{who}}, it's a template!

5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README-hieroglyph.rst: -------------------------------------------------------------------------------- 1 | ======================================== 2 | Creating Presentations with Hieroglyph 3 | ======================================== 4 | 5 | Hieroglyph (https://pypi.python.org/pypi/hieroglyph) is a Sphinx 6 | (https://pypi.python.org/pypi/Sphinx) builder for converting 7 | reStructuredText files to HTML slide presentations. It lets you write 8 | simple text files with headings, lists, and embedded code samples and 9 | then turns them into slide presentations for you. 10 | 11 | Example 12 | ======= 13 | 14 | First, you need a copy of cookiecutter 15 | (https://pypi.python.org/pypi/cookiecutter) installed. Cookiecutter 16 | uses templates to populate a directory with the files needed for a 17 | project. You can use the template for hieroglyph at 18 | https://github.com/dhellmann/cookiecutter-hieroglyph to create a basic 19 | presentation, which you can then modify with your own content. 20 | 21 | :: 22 | 23 | $ pip install cookiecutter 24 | 25 | Next, create a directory to contain the inputs for the 26 | presentation:: 27 | 28 | $ mkdir mypres 29 | $ cd mypres 30 | 31 | Now run cookiecutter to create the basic presentation files:: 32 | 33 | $ cookiecutter https://github.com/dhellmann/cookiecutter-hieroglyph 34 | config_path is /Users/dhellmann/.cookiecutterrc 35 | Cloning into 'cookiecutter-hieroglyph'... 36 | remote: Counting objects: 19, done. 37 | remote: Compressing objects: 100% (5/5), done. 38 | remote: Total 19 (delta 0), reused 0 (delta 0) 39 | Unpacking objects: 100% (19/19), done. 40 | Checking connectivity... done. 41 | full_name (default is "Doug Hellmann")? 42 | email (default is "doug@doughellmann.com")? 43 | twitter (default is "doughellmann")? 44 | repo_name (default is "talk")? 45 | meeting_date (default is "Date of meeting")? 2014-09 46 | title (default is "Title of your presentation")? A Whirlwind Tour of the Standard Library 47 | 48 | This creates a few files:: 49 | 50 | $ tree . 51 | . 52 | ├── Makefile 53 | ├── requirements.txt 54 | └── source 55 | ├── _static 56 | │ └── custom.css 57 | ├── conf.py 58 | └── index.rst 59 | 60 | 2 directories, 5 files 61 | 62 | Put your content in ``source/index.rst``, using some of the other 63 | hieroglyph-based presentations in this repository as examples. 64 | 65 | To build your slides, just run ``make``. It will use virtualenv 66 | (https://pypi.python.org/pypi/virtualenv) to install the tools it 67 | needs and then use those tools to convert your input file to HTML. 68 | 69 | :: 70 | 71 | $ make 72 | virtualenv .venv 73 | New python executable in .venv/bin/python 74 | Installing setuptools, pip...done. 75 | .venv/bin/pip install -r requirements.txt 76 | Downloading/unpacking Sphinx (from -r requirements.txt (line 1)) 77 | 78 | ... lots more download and installation output elided ... 79 | 80 | Successfully installed Sphinx hieroglyph flake8 Jinja2 Pygments docutils 81 | six pyflakes mccabe pep8 markupsafe 82 | Cleaning up... 83 | ./.venv/bin/sphinx-build -b slides -d build/doctrees source build/slides 84 | Making output directory... 85 | Running Sphinx v1.2.3 86 | loading pickled environment... not yet created 87 | building [slides]: targets for 1 source files that are out of date 88 | updating environment: 1 added, 0 changed, 0 removed 89 | reading sources... [100%] index 90 | looking for now-outdated files... none found 91 | pickling environment... done 92 | checking consistency... done 93 | preparing documents... done 94 | writing output... [100%] index 95 | writing additional files... genindex search 96 | copying static files... done 97 | copying extra files... done 98 | dumping search index... done 99 | dumping object inventory... done 100 | build succeeded, 1 warning. 101 | 102 | Build finished. The slides are in build/slides. 103 | 104 | The output presentation is in ``./build/slides/index.html``. All of 105 | the required files should be present under ``build/slides``, allowing 106 | you to give your presentation even without internet access. 107 | -------------------------------------------------------------------------------- /bin/dates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Print a list of the dates for the 2nd Thursday of every month for a year. 3 | """ 4 | 5 | from __future__ import print_function 6 | 7 | import argparse 8 | import calendar 9 | import datetime 10 | 11 | 12 | ap = argparse.ArgumentParser() 13 | ap.add_argument( 14 | 'year', 15 | type=int, 16 | nargs='?', 17 | default=datetime.date.today().year, 18 | help='the year', 19 | ) 20 | args = ap.parse_args() 21 | 22 | 23 | # Show every month 24 | for month in range(1, 13): 25 | 26 | # Compute the dates for each week that overlaps the month 27 | c = calendar.monthcalendar(args.year, month) 28 | first_week = c[0] 29 | second_week = c[1] 30 | third_week = c[2] 31 | 32 | # If there is a Thursday in the first week, the second Thursday 33 | # is in the second week. Otherwise, the second Thursday must 34 | # be in the third week. 35 | if first_week[calendar.THURSDAY]: 36 | meeting_date = second_week[calendar.THURSDAY] 37 | else: 38 | meeting_date = third_week[calendar.THURSDAY] 39 | 40 | print('%s %s' % (calendar.month_name[month], meeting_date)) 41 | --------------------------------------------------------------------------------