diff --git a/.buildinfo b/.buildinfo
new file mode 100644
index 00000000..1e0ee277
--- /dev/null
+++ b/.buildinfo
@@ -0,0 +1,4 @@
+# Sphinx build info version 1
+# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
+config: bc5cc1abd553588ef0bfa068e3299468
+tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 053ae06c..00000000
--- a/.gitignore
+++ /dev/null
@@ -1,107 +0,0 @@
-# mac annoying files..
-.DS_Store
-
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-env/
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-wheels/
-*.egg-info/
-.installed.cfg
-*.egg
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-.hypothesis/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# pyenv
-.python-version
-
-# celery beat schedule file
-celerybeat-schedule
-
-# SageMath parsed files
-*.sage.py
-
-# dotenv
-.env
-
-# virtualenv
-.venv
-venv/
-ENV/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
-
-# emacs
-*.*~
\ No newline at end of file
diff --git a/source/_static/dummy b/.nojekyll
similarity index 100%
rename from source/_static/dummy
rename to .nojekyll
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 8b88d6b6..00000000
--- a/LICENSE
+++ /dev/null
@@ -1,98 +0,0 @@
-Creative Commons Attribution-ShareAlike 4.0 International Public License
-
-By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
-
-Section 1 – Definitions.
-
-Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
-Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
-BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License.
-Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
-Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
-Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
-License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution and ShareAlike.
-Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
-Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
-Licensor means the individual(s) or entity(ies) granting rights under this Public License.
-Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
-Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
-You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
-Section 2 – Scope.
-
-License grant.
-Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
-reproduce and Share the Licensed Material, in whole or in part; and
-produce, reproduce, and Share Adapted Material.
-Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
-Term. The term of this Public License is specified in Section 6(a).
-Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
-Downstream recipients.
-Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
-Additional offer from the Licensor – Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply.
-No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
-No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
-Other rights.
-
-Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
-Patent and trademark rights are not licensed under this Public License.
-To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties.
-Section 3 – License Conditions.
-
-Your exercise of the Licensed Rights is expressly made subject to the following conditions.
-
-Attribution.
-
-If You Share the Licensed Material (including in modified form), You must:
-
-retain the following if it is supplied by the Licensor with the Licensed Material:
-identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
-a copyright notice;
-a notice that refers to this Public License;
-a notice that refers to the disclaimer of warranties;
-a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
-indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
-indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
-You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
-If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
-ShareAlike.
-In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply.
-
-The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-SA Compatible License.
-You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material.
-You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply.
-Section 4 – Sui Generis Database Rights.
-
-Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
-
-for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database;
-if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and
-You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
-For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
-Section 5 – Disclaimer of Warranties and Limitation of Liability.
-
-Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
-To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
-The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
-Section 6 – Term and Termination.
-
-This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
-Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
-
-automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
-upon express reinstatement by the Licensor.
-For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
-For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
-Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
-Section 7 – Other Terms and Conditions.
-
-The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
-Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
-Section 8 – Interpretation.
-
-For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
-To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
-No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
-Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
-Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
-
-Creative Commons may be contacted at creativecommons.org.
\ No newline at end of file
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 9d0976e6..00000000
--- a/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# Minimal makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-SPHINXPROJ = Py100
-SOURCEDIR = source
-BUILDDIR = build
-
-# Put it first so that "make" without argument is like "make help".
-help:
- @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-
-.PHONY: help Makefile
-
-# Catch-all target: route all unknown targets to Sphinx using the new
-# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
-%: Makefile
- @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
\ No newline at end of file
diff --git a/README.rst b/README.rst
deleted file mode 100644
index 671a94f3..00000000
--- a/README.rst
+++ /dev/null
@@ -1,12 +0,0 @@
-=======================================
-UWPCE Certificate In Python Programming
-=======================================
-
-This repository contains the source materials for the University of Washington Professional and Continuing Education Program Python Certification Program:
-
-`Certificate in Python Programming `_
-
-
-These materials are available in html rendered version here:
-
-https://uwpce-pythoncert.github.io/PythonCertDevel/
diff --git a/_downloads/ICanEatGlass.utf16.txt b/_downloads/ICanEatGlass.utf16.txt
new file mode 100644
index 00000000..24a0858d
Binary files /dev/null and b/_downloads/ICanEatGlass.utf16.txt differ
diff --git a/_downloads/ICanEatGlass.utf8.txt b/_downloads/ICanEatGlass.utf8.txt
new file mode 100644
index 00000000..9ecba2b9
--- /dev/null
+++ b/_downloads/ICanEatGlass.utf8.txt
@@ -0,0 +1,23 @@
+I Can Eat Glass:
+
+And from the sublime to the ridiculous, here is a certain phrase in an assortment of languages:
+
+Sanskrit: काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम् ॥
+
+Sanskrit (standard transcription): kācaṃ śaknomyattum; nopahinasti mām.
+
+Classical Greek: ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει.
+
+Greek (monotonic): Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα.
+
+Greek (polytonic): Μπορῶ νὰ φάω σπασμένα γυαλιὰ χωρὶς νὰ πάθω τίποτα.
+
+Latin: Vitrum edere possum; mihi non nocet.
+
+Old French: Je puis mangier del voirre. Ne me nuit.
+
+French: Je peux manger du verre, ça ne me fait pas mal.
+
+Provençal / Occitan: Pòdi manjar de veire, me nafrariá pas.
+
+Québécois: J'peux manger d'la vitre, ça m'fa pas mal.
\ No newline at end of file
diff --git a/_downloads/add_book_data.py b/_downloads/add_book_data.py
new file mode 100644
index 00000000..4ef65cfd
--- /dev/null
+++ b/_downloads/add_book_data.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+
+"""
+sample data for persistence/serializatiion examples
+
+This version is nested, with more stucture
+ - can be saved with pickle, JSON, xml...
+"""
+
+AddressBook = [ {'first_name': "Chris",
+ 'last_name': "Barker",
+ 'address' : {'line_1':"835 NE 33rd St",
+ 'line_2' : "",
+ 'city' : "Seattle",
+ 'state': "WA",
+ 'zip': "96543"},
+ 'email' : "PythonCHB@gmail.com",
+ 'home_phone' : "206-555-1234",
+ 'office_phone' : "123-456-7890",
+ 'cell_phone' : "234-567-8901",
+ },
+
+ {'first_name': "Fred",
+ 'last_name': "Jones",
+ 'address' : {'line_1':"123 SE 13th St",
+ 'line_2' : "Apt. 43",
+ 'city' : "Tacoma",
+ 'state': "WA",
+ 'zip': "93465"},
+ 'email' : "FredJones@some_company.com",
+ 'home_phone' : "510-555-1234",
+ 'office_phone' : "564-466-7990",
+ 'cell_phone' : "403-561-8911",
+ },
+
+ {'first_name': "Nancy",
+ 'last_name': "Wilson",
+ 'address' : {'line_1':"8654 Walnut St",
+ 'line_2' : "Suite 567",
+ 'city' : "Pasadena",
+ 'state': "CA",
+ 'zip': "12345"},
+ 'email' : "Wilson.Nancy@gmail.com",
+ 'home_phone' : "423-321-9876",
+ 'office_phone' : "123-765-9877",
+ 'cell_phone' : "432-567-8466",
+ },
+ ]
+
diff --git a/_downloads/add_book_data_flat.py b/_downloads/add_book_data_flat.py
new file mode 100644
index 00000000..97a0869d
--- /dev/null
+++ b/_downloads/add_book_data_flat.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+
+"""
+sample data for persistence/serialization examples
+this version is flat for saving in CSV, ini, etc.
+"""
+
+AddressBook = [ {'first_name': "Chris",
+ 'last_name': "Barker",
+ 'address_line_1':"835 NE 33rd St",
+ 'address_line_2' : "",
+ 'address_city' : "Seattle",
+ 'address_state': "WA",
+ 'address_zip': "96543",
+ 'email' : "PythonCHB@gmail.com",
+ 'home_phone' : "206-555-1234",
+ 'office_phone' : "123-456-7890",
+ 'cell_phone' : "234-567-8901",
+ },
+
+ {'first_name': "Fred",
+ 'last_name': "Jones",
+ 'address_line_1':"123 SE 13th St",
+ 'address_line_2' : "Apt. 43",
+ 'address_city' : "Tacoma",
+ 'address_state': "WA",
+ 'address_zip': "93465",
+ 'email' : "FredJones@some_company.com",
+ 'home_phone' : "510-555-1234",
+ 'office_phone' : "564-466-7990",
+ 'cell_phone' : "403-561-8911",
+ },
+
+ {'first_name': "Nancy",
+ 'last_name': "Wilson",
+ 'address_line_1':"8654 Walnut St",
+ 'address_line_2' : "Suite 567",
+ 'address_city' : "Pasadena",
+ 'address_state': "CA",
+ 'address_zip': "12345",
+ 'email' : "Wilson.Nancy@gmail.com",
+ 'home_phone' : "423-321-9876",
+ 'office_phone' : "123-765-9877",
+ 'cell_phone' : "432-567-8466",
+ },
+ ]
+
diff --git a/_downloads/address_book_model.py b/_downloads/address_book_model.py
new file mode 100644
index 00000000..6cc28cbe
--- /dev/null
+++ b/_downloads/address_book_model.py
@@ -0,0 +1,266 @@
+#!/usr/bin/env python
+
+"""
+sample data for NOSQL examples
+
+This version has a not completely-trival data model
+"""
+
+
+class Person(object):
+ """
+ class to represent an individual person
+ """
+
+ def __init__(self,
+ last_name,
+ first_name='',
+ middle_name='',
+ cell_phone='',
+ email='',
+ ):
+ """
+ initialize a Person object:
+ """
+ self.first_name = first_name.strip()
+ self.last_name = last_name.strip()
+ self.middle_name = middle_name.strip()
+ self.cell_phone = cell_phone.strip()
+ self.email = email.strip()
+
+ @property
+ def name(self):
+ return " ".join([self.first_name, self.middle_name, self.last_name])
+
+ def __str__(self):
+ msg = '{first_name} {middle_name} {last_name}'.format(**self.__dict__)
+ return msg
+
+ def __repr__(self):
+ """
+ not a good ___repr__, but want to have something here
+ """
+ return self.__str__()
+
+
+class Address(object):
+ """
+ class that represents an address
+ """
+
+ def __init__(self,
+ line_1='',
+ line_2='',
+ city='',
+ state='',
+ zip_code='',
+ ):
+ """
+ initialize an address
+ """
+
+ self.line_1 = line_1.strip()
+ self.line_2 = line_2.strip()
+ self.city = city.strip()
+ self.state = state.strip()
+ self.zip_code = str(zip_code).strip()
+
+ def __str__(self):
+ msg = "{line_1}\n{line_2}\n{city} {state} {zip_code}\n".format(**self.__dict__)
+ return msg
+
+
+class Household(object):
+ """
+ Class that represents a Household.
+
+ A household has one or more people, and a Location
+ """
+
+ def __init__(self,
+ name='',
+ people=(),
+ address=None,
+ phone=''
+ ):
+ self.name = name.strip()
+ self.people = list(people)
+ self.address = address
+ self.phone = phone.strip()
+
+ def __str__(self):
+ msg = [self.name + ":"]
+ msg += [str(self.address)]
+ return "\n".join(msg)
+
+ def __repr__(self):
+ return self.__str__()
+
+
+class Business(Household):
+ """
+ Class that represents a Business
+
+ A business has one or more people,
+ and address and a phone number
+ """
+ # Same as household now, but you never know.
+ pass
+
+
+class AddressBook(object):
+ """
+ And address book -- has people, households, businesses.
+
+ All fully cross-referenced
+ """
+
+ def __init__(self,
+ people=(),
+ businesses=(),
+ households=(),
+ ):
+ self.people = list(people)
+ self.businesses = list(businesses)
+ self.households = list(households)
+
+ def add_person(self, person):
+ self.people.append(person)
+
+ def add_household(self, household):
+ self.households.append(household)
+
+ def add_business(self, business):
+ self.businesses.append(business)
+
+ def __str__(self):
+ msg = ["An Address Book:"]
+ msg += ["People:"]
+ msg += [" " + person.name for person in self.find_people()]
+ msg += ["Households:"]
+ msg += [" " + house.name for house in self.find_households()]
+ msg += ["Businesses:"]
+ msg += [" " + bus.name for bus in self.find_businesses()]
+
+ return "\n".join(msg)
+
+ @property
+ def locations(self):
+ return self.households + self.businesses
+
+ def find_people(self, name=''):
+ """
+ find all the people with name in their name somewhere
+ """
+ return [person for person in self.people if name.lower() in person.name.lower()]
+
+ def find_zip_codes(self, zip_code):
+ """
+ find all the locations with this zip_code
+ """
+ zip_code = str(zip_code).strip()
+ return [loc for loc in self.locations if loc.address.zip_code == zip_code]
+
+ def find_state(self, state):
+ """
+ find all the locations in this state
+ """
+ return [location for location in self.locations if location.address.state == state]
+
+
+def create_sample():
+ """
+ Create a sample Address Book
+ """
+ chris = Person(last_name='Barker',
+ first_name='Chris',
+ middle_name='H',
+ cell_phone='(123) 555-7890',
+ email='PythonCHB@gmail.com',
+ )
+
+ emma = Person(last_name='Barker',
+ first_name='Emma',
+ middle_name='L',
+ cell_phone='(345) 555-9012',
+ email='emma@something.com',
+ )
+
+ donna = Person(last_name='Barker',
+ first_name='Donna',
+ middle_name='L',
+ cell_phone='(111) 555-1111',
+ email='dbarker@something.com',
+ )
+
+ barker_address = Address(line_1='123 Some St',
+ line_2='Apt 1234',
+ city='Seattle',
+ state='WA',
+ zip_code='98000',)
+
+ the_barkers = Household(name="The Barkers",
+ people=(chris, donna, emma),
+ address=barker_address)
+
+ joseph = Person(last_name='Sheedy',
+ first_name='Joseph',
+ cell_phone='(234) 555-8910',
+ email='js@some_thing.com',
+ )
+
+ cris = Person(last_name='Ewing',
+ first_name='Cris',
+ cell_phone='(345) 555-6789',
+ email='cris@a_fake_domain.com',
+ )
+
+ fulvio = Person(last_name='Casali',
+ first_name='Fulvio',
+ cell_phone='(345) 555-1234',
+ email='fulvio@a_fake_domain.com',
+ )
+
+ fred = Person(first_name="Fred",
+ last_name="Jones",
+ email='FredJones@some_company.com',
+ cell_phone="403-561-8911",
+ )
+
+ python_cert_address = Address('UW Professional and Continuing Education',
+ line_2='4333 Brooklyn Ave. NE',
+ city='Seattle',
+ state='WA',
+ zip_code='98105',
+ )
+
+ python_cert = Business(name='Python Certification Program',
+ people=(chris, joseph, cris, fulvio),
+ address=python_cert_address
+ )
+
+
+ address_book = AddressBook()
+
+ address_book.add_person(chris)
+ address_book.add_person(donna)
+ address_book.add_person(emma)
+ address_book.add_person(cris)
+ address_book.add_person(joseph)
+ address_book.add_person(fulvio)
+ address_book.add_person(fred)
+
+ address_book.add_household(the_barkers)
+
+ address_book.add_business(python_cert)
+
+ return address_book
+
+
+if __name__ == "__main__":
+ address_book = create_sample()
+
+ print("Here is the address book")
+ print(address_book)
+
+
diff --git a/_downloads/address_book_mongo.py b/_downloads/address_book_mongo.py
new file mode 100644
index 00000000..9d0a96ac
--- /dev/null
+++ b/_downloads/address_book_mongo.py
@@ -0,0 +1,367 @@
+#!/usr/bin/env python
+
+"""
+sample data for NOSQL examples
+
+This version uses mongoDB to store the data.
+
+NOTE: you need to start up the DB first:
+
+$ mongod --dbpath=mongo_data/
+
+"""
+
+# ObjectID provides a new unique ID when you need one.
+from bson import ObjectId
+
+
+class PersistObject:
+ """
+ mix-in class for object you want to be able to put in a mongoDB
+
+ defines the basic to_dict and from_dict methods
+
+ This could be a metaclass, or class decorator, or...
+ """
+ def to_dict(self):
+ """
+ returns a dictionary of all the data in the object
+
+ pretty simple in this case, but might be more to it
+ for a more complicated class
+ """
+ return self.__dict__
+
+ @classmethod
+ def from_dict(cls, dct):
+ """
+ Returns a new object initialized from the values in dct
+
+ Just calls the usual __init__ in this case. but could be more
+ to it in a more complex case.
+ """
+ return cls(**dct)
+
+
+class Person(PersistObject):
+ """
+ class to represent an individual person
+ """
+ def __init__(self,
+ last_name,
+ first_name='',
+ middle_name='',
+ cell_phone='',
+ email='',
+ _id=None,
+ ):
+ """
+ initialize a Person object:
+ """
+ self.first_name = first_name.strip()
+ self.last_name = last_name.strip()
+ self.middle_name = middle_name.strip()
+ self.cell_phone = cell_phone.strip()
+ self.email = email.strip()
+ self._id = ObjectId() if _id is None else _id
+
+ @property
+ def name(self):
+ return " ".join([self.first_name, self.middle_name, self.last_name])
+
+ def __str__(self):
+ msg = '{first_name} {middle_name} {last_name}'.format(**self.__dict__)
+ return msg
+
+ def __repr__(self):
+ """
+ Not a good ___repr__, but want to have something here
+ """
+ return self.__str__()
+
+
+class Address(PersistObject):
+ """
+ class that represents an address
+ """
+ def __init__(self,
+ line_1='',
+ line_2='',
+ city='',
+ state='',
+ zip_code='',
+# **kwargs
+ ):
+ """
+ Initialize an address
+ """
+
+ self.line_1 = line_1.strip()
+ self.line_2 = line_2.strip()
+ self.city = city.strip()
+ self.state = state.strip().upper()
+ self.zip_code = str(zip_code).strip()
+
+ def __str__(self):
+ msg = "{line_1}\n{line_2}\n{city} {state} {zip_code}\n".format(**self.__dict__)
+ return msg
+
+
+class Household(PersistObject):
+ """
+ Class that represents a Household.
+
+ A household has one or more people, and a Location
+ """
+
+ def __init__(self,
+ name = '',
+ people=(),
+ address=None,
+ phone='',
+ **kwargs
+ ):
+
+ self.name = name.strip()
+ # if it's already a ObjectID, then don't need to extract it.
+ try:
+ self.people = [p._id for p in people]
+ except AttributeError:
+ self.people = people
+ self.address = address
+ self.phone = phone.strip()
+
+ def to_dict(self):
+ """
+ convert to dict -- stores ids of people
+ """
+ dct = self.__dict__
+ dct['address'] = self.address.to_dict()
+ return dct
+
+ @classmethod
+ def from_dict(cls, dct):
+ """
+ creates a household object from a dict representation
+ unpacks the people _ids and address.
+ """
+ dct['address'] = Address(**dct['address'])
+ return cls(**dct)
+
+ def __str__(self):
+ msg = [self.name + ":"]
+ msg += [str(self.address)]
+ return "\n".join(msg)
+
+ def __repr__(self):
+ return self.__str__()
+
+
+class Business(Household):
+ """
+ Class that represents a Business
+
+ A business has one or more people,
+ and address and a phone number
+ """
+ # Same as household now, but you never know.
+ pass
+
+
+class AddressBook(object):
+ """
+ An address book -- has people, households, businesses.
+
+ All cross-referenced (normalized)
+ """
+
+ def __init__(self,
+ people=(),
+ businesses=(),
+ households=(),
+ fresh=True,
+ ):
+
+ # create the DB
+ from pymongo import MongoClient
+
+ client = MongoClient('localhost', 27017)
+ db = client.address_book
+ if fresh:
+ ## clean out old one
+ db.people.drop()
+ db.businesses.drop()
+ db.households.drop()
+
+ # Use the DB to hold the data
+ self.people = db.people
+ self.businesses = db.businesses
+ self.households = db.households
+
+ def add_person(self, person):
+ self.people.insert(person.to_dict())
+
+ def add_household(self, household):
+ self.households.insert(household.to_dict())
+
+ def add_business(self, business):
+ self.businesses.insert(business.to_dict())
+
+ def __str__(self):
+ msg = ["An Address Book:"]
+ msg += ["People:"]
+ msg += [" " + person.name for person in self.find_people()]
+ msg += ["Households:"]
+ msg += [" " + house.name for house in self.find_households()]
+ msg += ["Businesses:"]
+ msg += [" " + bus.name for bus in self.find_businesses()]
+
+ return "\n".join(msg)
+
+ def find_people(self, name=''):
+ """
+ Find all the people with name in their name somewhere
+ """
+ # fixme -- can this query be combined?
+ # like this: db.inventory.find( { $or: [ { qty: { $lt: 20 } }, { sale: true } ] } )
+
+ cursor = self.people.find({"first_name": {'$regex': '.*' + name + '.*',
+ '$options': 'i'}})
+ results = [Person.from_dict(p) for p in cursor]
+
+ cursor = self.people.find({"last_name": {'$regex': '.*' + name + '.*',
+ '$options': 'i'}})
+
+ return results + [Person.from_dict(p) for p in cursor]
+
+ def find_households(self):
+ cursor = self.households.find()
+ return [Household.from_dict(p) for p in cursor]
+
+ def find_businesses(self):
+ cursor = self.businesses.find()
+ return [Business.from_dict(p) for p in cursor]
+
+ def find_zip_codes(self, zip_code):
+ """
+ find all the locations with this zip_code
+ """
+ zip_code = str(zip_code).strip()
+ cursor = self.households.find({"addresses.zip_code": zip_code})
+ results = [Household.from_dict(dct) for dct in cursor]
+
+ cursor = self.businesses.find({"address.zip_code": zip_code})
+ results += [Business.from_dict(dct) for dct in cursor]
+
+ return results
+
+ def find_state(self, state):
+ """
+ find all the locations in this state
+ """
+ state = state.strip().upper()
+ cursor = self.households.find({"address.state": state})
+ results = [Household.from_dict(dct) for dct in cursor]
+
+ cursor = self.businesses.find({"address.state": state})
+ results += [Business.from_dict(dct) for dct in cursor]
+
+ return results
+
+
+def create_sample():
+ """
+ Create a sample Address Book
+ """
+ chris = Person(last_name='Barker',
+ first_name='Chris',
+ middle_name='H',
+ cell_phone='(123) 555-7890',
+ email='PythonCHB@gmail.com',
+ )
+
+ emma = Person(last_name='Barker',
+ first_name='Emma',
+ middle_name='L',
+ cell_phone='(345) 555-9012',
+ email='emma@something.com',
+ )
+
+ donna = Person(last_name='Barker',
+ first_name='Donna',
+ middle_name='L',
+ cell_phone='(111) 555-1111',
+ email='dbarker@something.com',
+ )
+
+ barker_address = Address(line_1='123 Some St',
+ line_2='Apt 1234',
+ city='Seattle',
+ state='WA',
+ zip_code='98000',)
+
+ the_barkers = Household(name="The Barkers",
+ people=(chris, donna, emma),
+ address=barker_address)
+
+ joseph = Person(last_name='Sheedy',
+ first_name='Joseph',
+ cell_phone='(234) 555-8910',
+ email='js@some_thing.com',
+ )
+
+ cris = Person(last_name='Ewing',
+ first_name='Cris',
+ cell_phone='(345) 555-6789',
+ email='cris@a_fake_domain.com',
+ )
+
+ fulvio = Person(last_name='Casali',
+ first_name='Fulvio',
+ cell_phone='(345) 555-1234',
+ email='fulvio@a_fake_domain.com',
+ )
+
+ fred = Person(first_name="Fred",
+ last_name="Jones",
+ email='FredJones@some_company.com',
+ cell_phone="403-561-8911",
+ )
+
+ python_cert_address = Address('UW Professional and Continuing Education',
+ line_2='4333 Brooklyn Ave. NE',
+ city='Seattle',
+ state='WA',
+ zip_code='98105',
+ )
+
+ python_cert = Business(name='Python Certification Program',
+ people=(chris, joseph, cris, fulvio),
+ address=python_cert_address
+ )
+
+
+ address_book = AddressBook()
+
+ address_book.add_person(chris)
+ address_book.add_person(donna)
+ address_book.add_person(emma)
+ address_book.add_person(cris)
+ address_book.add_person(joseph)
+ address_book.add_person(fulvio)
+ address_book.add_person(fred)
+
+ address_book.add_household(the_barkers)
+
+ address_book.add_business(python_cert)
+
+ return address_book
+
+
+if __name__ == "__main__":
+ address_book = create_sample()
+
+ print("Here is the address book")
+ print(address_book)
+
+
diff --git a/_downloads/async_executor.py b/_downloads/async_executor.py
new file mode 100644
index 00000000..d0d33cb7
--- /dev/null
+++ b/_downloads/async_executor.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+
+"""
+An example of runing a blocking task in an Executor:
+"""
+
+import asyncio
+import time
+import datetime
+import random
+
+
+async def small_task(num):
+ """
+ Just something to give us little tasks that run at random intervals
+ These will go on forever
+ """
+ while True: # keep doing this until break
+ print("task: {} run".format(num))
+ # pause for a random amount of time between 0 and 2 seconds
+ await asyncio.sleep(random.random() * 2)
+
+async def slow_task():
+ while True: # keep going forever
+ print("running the slow task- blocking!")
+ # This will block for 2-10 seconds!
+ # result = slow_function(random.random() * 8 + 2)
+ # uncomment to put it on a different thread:
+ # result = slow_function(random.random() * 8 + 2)
+ result = await loop.run_in_executor(None,
+ slow_function,
+ random.random() * 8 + 2)
+ print("slow function done: result", result)
+ # await asyncio.sleep(0.0) # to release the loop
+
+
+def slow_function(duration):
+ """
+ this is a fake function that takes a long time, and blocks
+ """
+ time.sleep(duration)
+ print("slow task complete")
+ return duration
+
+
+# get a loop going:
+loop = asyncio.get_event_loop()
+
+# or add tasks to the loop like this:
+loop.create_task(small_task(1))
+loop.create_task(small_task(2))
+loop.create_task(small_task(3))
+loop.create_task(small_task(4))
+
+# Add the slow one
+loop.create_task(slow_task())
+
+print("about to run loop")
+# this is a blocking call
+# we will need to hit ^C to stop it...
+loop.run_forever()
+print("loop exited")
diff --git a/_downloads/async_timer.py b/_downloads/async_timer.py
new file mode 100644
index 00000000..2cbe756f
--- /dev/null
+++ b/_downloads/async_timer.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+
+"""
+Simple async example derived from python docs.
+
+Will only work on Python 3.5 and above
+"""
+
+import asyncio
+import time
+import datetime
+import random
+
+
+# using "async" makes this a coroutine:
+# its code can be run by the event loop
+async def display_date(num):
+ end_time = time.time() + 10.0 # we want it to run for 10 seconds.
+ while True: # keep doing this until break
+ print("instance: {} Time: {}".format(num, datetime.datetime.now()))
+ if (time.time()) >= end_time:
+ print("instance: {} is all done".format(num))
+ break
+ # pause for a random amount of time
+ await asyncio.sleep(random.randint(0, 3))
+
+
+def shutdown():
+ print("shutdown called")
+ # you can access the event loop this way:
+ loop = asyncio.get_event_loop()
+ loop.stop()
+
+
+# You register "futures" on the loop this way:
+asyncio.ensure_future(display_date(1))
+asyncio.ensure_future(display_date(2))
+
+loop = asyncio.get_event_loop()
+
+# or add tasks to the loop like this:
+loop.create_task(display_date(3))
+loop.create_task(display_date(4))
+for i in range(5, 20):
+ loop.create_task(display_date(i))
+
+# this will shut the event loop down in 15 seconds
+loop.call_later(15, shutdown)
+
+print("about to run loop")
+# this is a blocking call
+loop.run_forever()
+print("loop exited")
+
diff --git a/_downloads/capitalize.zip b/_downloads/capitalize.zip
new file mode 100644
index 00000000..2c7d361a
Binary files /dev/null and b/_downloads/capitalize.zip differ
diff --git a/_downloads/context_manager.py b/_downloads/context_manager.py
new file mode 100644
index 00000000..7e6f74ca
--- /dev/null
+++ b/_downloads/context_manager.py
@@ -0,0 +1,21 @@
+# Demo of a contextmanager
+
+
+class Context(object):
+ """
+ from Doug Hellmann, PyMOTW
+ https://pymotw.com/3/contextlib/#module-contextlib
+ """
+
+ def __init__(self, handle_error):
+ print('__init__({})'.format(handle_error))
+ self.handle_error = handle_error
+
+ def __enter__(self):
+ print('__enter__()')
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ print(exc_val.args)
+ print('__exit__({}, {}, {})'.format(exc_type, exc_val, exc_tb))
+ return self.handle_error
diff --git a/_downloads/cool_meta.py b/_downloads/cool_meta.py
new file mode 100644
index 00000000..ef3e8217
--- /dev/null
+++ b/_downloads/cool_meta.py
@@ -0,0 +1,30 @@
+"""
+Complete do-nothing metaclass example
+
+It serves to show when each special method of the metaclass is called.
+
+"""
+
+
+class CoolMeta(type):
+ def __new__(meta, name, bases, dct):
+ print('Creating class in CoolMeta.__new__', name)
+ return super().__new__(meta, name, bases, dct)
+
+ def __init__(cls, name, bases, dct):
+ print('Initializing class in CoolMeta.__init__', name)
+ super().__init__(name, bases, dct)
+
+ def __call__(cls, *args, **kw):
+ print('calling CoolMeta to instantiate ', cls)
+ return type.__call__(cls, *args, **kw)
+
+
+class CoolClass(metaclass=CoolMeta):
+ def __init__(self):
+ print('And now my CoolClass object exists')
+
+
+print('everything loaded, instantiate a CoolClass instance now')
+
+foo = CoolClass()
diff --git a/_downloads/diamond.py b/_downloads/diamond.py
new file mode 100644
index 00000000..b994c3ad
--- /dev/null
+++ b/_downloads/diamond.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+"""
+example of the classic "diamond problem"
+
+In this case, class A is at the root of the class hierarchy
+
+B and C both inherit from A
+
+D inherits from B and C
+
+ASCII art that shows this:
+
+ -----
+ | A |
+ -----
+ / \
+ / \
+----- -----
+| B | | C |
+----- -----
+ \ /
+ \ /
+ -----
+ | D |
+ -----
+
+So what's the problem?
+
+If you call a method on D -- it calls B and C's method -- and each
+of them call A's method. So A's method gets called twice!
+
+The reason this is a tricky is that when you write the D class, you may not
+know that B and C both inherit from A. And you may indeed *need* to call both
+B and C's method.
+
+"""
+
+
+class A(object):
+ def do_your_stuff(self):
+ print("doing A's stuff")
+
+
+class Default(A):
+ def do_your_stuff(self):
+ print('doing Default stuff')
+
+
+class B(A):
+ def do_your_stuff(self):
+ A.do_your_stuff(self)
+ print("doing B's stuff")
+
+
+class C(A):
+ def do_your_stuff(self):
+ A.do_your_stuff(self)
+ print("doing C's stuff")
+
+
+class D(B, C):
+ def do_your_stuff(self):
+ B.do_your_stuff(self)
+ C.do_your_stuff(self)
+ print("doing D's stuff")
+
+
+if __name__ == '__main__':
+ a = A()
+ print("\ncalling A's method")
+ a.do_your_stuff()
+
+ default = Default()
+ print("\ncalling Default's method")
+ default.do_your_stuff()
+
+ print("\ncalling B's method")
+ b = B()
+ b.do_your_stuff()
+
+ print("\ncalling C's method")
+ c = C()
+ c.do_your_stuff()
+
+ print("\ncalling D's method")
+ d = D()
+ d.do_your_stuff()
diff --git a/_downloads/diamond_super.py b/_downloads/diamond_super.py
new file mode 100644
index 00000000..ca5d929b
--- /dev/null
+++ b/_downloads/diamond_super.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+
+"""
+Example of solving the classic "diamond problem" using super()
+
+In this case, class A is at the root of the class hierarchy
+
+B and C both inherit from A
+
+D inherits from B and C
+
+ASCII art that shows this:
+
+ -----
+ | A |
+ -----
+ / \
+ / \
+----- -----
+| B | | C |
+----- -----
+ \ /
+ \ /
+ -----
+ | D |
+ -----
+
+So what's the problem?
+
+If you call a method on D -- it calls B and C's method -- and each
+of them call A's method. So A's method gets called twice!
+
+But using super() makes sure all the methods get called, but none of them twice.
+"""
+
+
+class A(object):
+ def do_your_stuff(self):
+ print("doing A's stuff")
+
+
+class Default(A):
+ def do_your_stuff(self):
+ print('doing Default stuff')
+
+
+class B(A):
+ def do_your_stuff(self):
+ super().do_your_stuff()
+ print("doing B's stuff")
+
+
+class C(A):
+ def do_your_stuff(self):
+ super().do_your_stuff()
+ print("doing C's stuff")
+
+
+class D(B, C):
+ def do_your_stuff(self):
+ super().do_your_stuff()
+ print("doing D's stuff")
+
+
+if __name__ == '__main__':
+ a = A()
+ print("\ncalling A's method")
+ a.do_your_stuff()
+
+ default = Default()
+ print("\ncalling Default's method")
+ default.do_your_stuff()
+
+ print("\ncalling B's method")
+ b = B()
+ b.do_your_stuff()
+
+ print("\ncalling C's method")
+ c = C()
+ c.do_your_stuff()
+
+ print("\ncalling D's method")
+ d = D()
+ d.do_your_stuff()
diff --git a/_downloads/eggs.csv b/_downloads/eggs.csv
new file mode 100644
index 00000000..efb5b9e7
--- /dev/null
+++ b/_downloads/eggs.csv
@@ -0,0 +1,3 @@
+Spam, Spam, Spam, Spam, Spam, Baked Beans
+Spam, "Lovely, Spam", Wonderful, Spam, and, more
+
diff --git a/_downloads/except_exercise.py b/_downloads/except_exercise.py
new file mode 100644
index 00000000..96c88709
--- /dev/null
+++ b/_downloads/except_exercise.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+
+"""
+An exercise in playing with Exceptions.
+Make lots of try/except blocks for fun and profit.
+
+Make sure to catch specifically the error you find, rather than all errors.
+"""
+
+from except_test import fun, more_fun, last_fun
+
+
+# Figure out what the exception is, catch it and while still
+# in that catch block, try again with the second item in the list
+first_try = ['spam', 'cheese', 'mr death']
+
+joke = fun(first_try[0])
+
+# Here is a try/except block. Add an else that prints not_joke
+try:
+ not_joke = fun(first_try[2])
+except SyntaxError:
+ print('Run Away!')
+
+# What did that do? You can think of else in this context, as well as in
+# loops as meaning: "else if nothing went wrong"
+# (no breaks in loops, no exceptions in try blocks)
+
+# Figure out what the exception is, catch it and in that same block
+#
+# try calling the more_fun function with the 2nd language in the list,
+# again assigning it to more_joke.
+#
+# If there are no exceptions, call the more_fun function with the last
+# language in the list
+
+# Finally, while still in the try/except block and regardless of whether
+# there were any exceptions, call the function last_fun with no
+# parameters. (pun intended)
+
+langs = ['java', 'c', 'python']
+
+more_joke = more_fun(langs[0])
diff --git a/_downloads/except_test.py b/_downloads/except_test.py
new file mode 100644
index 00000000..905dd675
--- /dev/null
+++ b/_downloads/except_test.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+
+"""
+silly little test module that is designed to trigger Exceptions when
+run from the except_exercise.py file
+"""
+
+import time
+
+conclude = "And what leads you to that conclusion?"
+district = "Finest in the district, sir."
+cheese = "It's certainly uncontaminated by cheese."
+clean = "Well, it's so clean."
+shop = "Not much of a cheese shop really, is it?"
+cust = "Customer: "
+clerk = "Shopkeeper: "
+
+
+def fun(reaper):
+ if reaper == 'spam':
+ print(s)
+ elif reaper == 'cheese':
+ print()
+ print('Spam, Spam, Spam, Spam, Beautiful Spam')
+ elif reaper == 'mr death':
+ print()
+ return('{}{}\n{}{}'.format(cust, shop, clerk, district))
+
+
+def more_fun(language):
+ if language == 'java':
+ test = [1, 2, 3]
+ test[5] = language
+ elif language == 'c':
+ print('{}{}\n{}{}'.format(cust, conclude, clerk, clean))
+
+
+def last_fun():
+ print(cust, cheese)
+ time.sleep(1)
+ import antigravity
diff --git a/_downloads/exception_test.py b/_downloads/exception_test.py
new file mode 100644
index 00000000..975f1df8
--- /dev/null
+++ b/_downloads/exception_test.py
@@ -0,0 +1,16 @@
+#!/usr/bin/python
+
+"""
+example for what happens when you pass non-ascii unicode to a Exception
+"""
+
+msg = u'This is an ASCII-compatible unicode message'
+
+#msg = u'This is an non ASCII\N{EM DASH}compatible unicode message'
+
+print "\nDo you see this message in the Exception report?\n"
+print msg
+print
+
+raise ValueError(msg)
+
diff --git a/_downloads/file_yielder.py b/_downloads/file_yielder.py
new file mode 100644
index 00000000..d0368343
--- /dev/null
+++ b/_downloads/file_yielder.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+
+import pathlib
+
+
+def file_yielder(dir=".", pattern="*"):
+ """
+ iterate over all the files that match the pattern
+
+ pattern us a "glob" pattern, like: *.py
+ """
+ for filename in pathlib.Path(dir).glob(pattern):
+ with open(filename) as file_obj:
+ yield file_obj
diff --git a/_downloads/gather.py b/_downloads/gather.py
new file mode 100644
index 00000000..c5e4b312
--- /dev/null
+++ b/_downloads/gather.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+
+"""
+test of gather()
+
+adapted from:
+
+https://docs.python.org/3/library/asyncio-task.html
+
+"""
+
+import asyncio
+
+
+async def factorial(name, number):
+ f = 1
+ for i in range(2, number + 1):
+ print("Task %s: Compute factorial(%s)..." % (name, i))
+ await asyncio.sleep(1) # to simulate a longer process...
+ f *= i
+ print("Task %s: factorial(%s) = %s" % (name, number, f))
+
+loop = asyncio.get_event_loop()
+loop.run_until_complete(asyncio.gather(factorial("A", 2),
+ factorial("B", 3),
+ factorial("C", 4),
+ ))
+loop.close()
diff --git a/_downloads/get_news_async.py b/_downloads/get_news_async.py
new file mode 100644
index 00000000..8641e0af
--- /dev/null
+++ b/_downloads/get_news_async.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+
+"""
+An Asynchronous version of the script to see how much a given word is
+mentioned in the news today
+
+Uses data from the NewsAPI:
+
+https://newsapi.org
+"""
+
+import time
+import asyncio
+import aiohttp
+
+NEWS_API_KEY = "84d0483394c44f288965d7b366e54a74"
+
+WORD = "war"
+base_url = 'https://newsapi.org/v1/'
+
+
+# This has to run first, so doesn't really need async
+# but why use two requests libraries ?
+async def get_sources(sources):
+ """
+ Get all the english language sources of news
+
+ 'https://newsapi.org/v1/sources?language=en'
+ """
+ url = base_url + "sources"
+ params = {"language": "en", "apiKey": NEWS_API_KEY}
+ async with aiohttp.ClientSession() as session:
+ async with session.get(url, ssl=False, params=params) as resp:
+ data = await resp.json()
+ print("Got the sources")
+ sources.extend([src['id'].strip() for src in data['sources']])
+
+
+async def get_articles(source):
+ """
+ download the info for all the articles
+ """
+ url = base_url + "articles"
+ params = {"source": source,
+ "apiKey": NEWS_API_KEY,
+ "sortBy": "top"
+ }
+ print("requesting:", source)
+ async with aiohttp.ClientSession() as session:
+ async with session.get(url, ssl=False, params=params) as resp:
+ if resp.status != 200: # aiohttpp has "status"
+ print(f'something went wrong with: {source}')
+ await asyncio.sleep(0) # releases control the the mainloop
+ return
+ # awaits response rather than waiting on response in the requests version of this
+ print("got the articles from {}".format(source))
+ data = await resp.json()
+ # the url to the article itself is in data['articles'][i]['url']
+ titles.extend([(str(art['title']) + str(art['description']))
+ for art in data['articles']])
+
+
+def count_word(word, titles):
+ word = word.lower()
+ count = 0
+ for title in titles:
+ if word in title.lower():
+ count += 1
+ return count
+
+
+start = time.time()
+
+# start up a loop:
+loop = asyncio.get_event_loop()
+
+# create the objects to hold the data
+sources = []
+titles = []
+
+# get the sources -- this is essentially synchronous
+loop.run_until_complete(get_sources(sources))
+
+# run the loop for the articles
+jobs = asyncio.gather(*(get_articles(source) for source in sources))
+loop.run_until_complete(jobs)
+loop.close()
+
+art_count = len(titles)
+word_count = count_word(WORD, titles)
+
+print(f'found {WORD}, {word_count} times in {art_count} articles')
+print(f'Process took {(time.time() - start):.0f} sec.')
diff --git a/_downloads/get_news_sync.py b/_downloads/get_news_sync.py
new file mode 100644
index 00000000..82a7b13b
--- /dev/null
+++ b/_downloads/get_news_sync.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+
+"""
+Regular synchronous script to see how much a given word is mentioned in the
+news today
+
+Took about 21 seconds for me.
+
+Uses data from the NewsAPI:
+
+https://newsapi.org
+
+NOTE: you need to register with the web site to get a KEY.
+"""
+import time
+import requests
+
+WORD = "trump"
+
+NEWS_API_KEY = "84d0483394c44f288965d7b366e54a74"
+
+base_url = 'https://newsapi.org/v1/'
+
+
+def get_sources():
+ """
+ Get all the english language sources of news
+
+ 'https://newsapi.org/v1/sources?language=en'
+ """
+ url = base_url + "sources"
+ params = {"language": "en"}
+ resp = requests.get(url, params=params)
+ data = resp.json()
+ sources = [src['id'].strip() for src in data['sources']]
+ print("all the sources")
+ print(sources)
+ return sources
+
+
+def get_articles(source):
+ """
+ https://newsapi.org/v1/articles?source=associated-press&sortBy=top&apiKey=1fabc23bb9bc485ca59b3966cbd6ea26
+ """
+ url = base_url + "articles"
+ params = {"source": source,
+ "apiKey": NEWS_API_KEY,
+ # "sortBy": "latest", # some sources don't support latest
+ "sortBy": "top",
+ # "sortBy": "popular",
+ }
+ print("requesting:", source)
+ resp = requests.get(url, params=params)
+ if resp.status_code != 200: # aiohttpp has "status"
+ print("something went wrong with {}".format(source))
+ print(resp)
+ print(resp.text)
+ return []
+ data = resp.json()
+ # the url to the article itself is in data['articles'][i]['url']
+ titles = [str(art['title']) + str(art['description'])
+ for art in data['articles']]
+ return titles
+
+
+def count_word(word, titles):
+ word = word.lower()
+ count = 0
+ for title in titles:
+ if word in title.lower():
+ count += 1
+ return count
+
+
+start = time.time()
+sources = get_sources()
+
+art_count = 0
+word_count = 0
+for source in sources:
+ titles = get_articles(source)
+ art_count += len(titles)
+ word_count += count_word('trump', titles)
+
+print(WORD, "found {} times in {} articles".format(word_count, art_count))
+print("Process took {:.0f} seconds".format(time.time() - start))
diff --git a/_downloads/get_set_attr.py b/_downloads/get_set_attr.py
new file mode 100644
index 00000000..30beb773
--- /dev/null
+++ b/_downloads/get_set_attr.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+
+"""
+Manipulating attributes
+
+Example code for manipulating attributes
+"""
+
+
+# A simple class for a person
+class Person:
+ def __init__(self, first_name="", last_name="", phone=""):
+ self.first_name = first_name
+ self.last_name = last_name
+ self.phone = phone
+
+ def __str__(self):
+ msg = ["Person:"]
+ for name, val in vars(self).items():
+ msg.append("{}: {}".format(name, val))
+ return "\n".join(msg)
+
+
+def update_person(person):
+ while True:
+ att = input("What would you like to update for:\n"
+ "{}\n"
+ '(type "quit" to quit) >> '.format(person)
+ )
+ if att.strip().lower() == "quit":
+ break
+ if not hasattr(person, att):
+ ans = input("This person does not have that attribute.\n"
+ "Would you like to add it? Y,[N] >> ")
+ if not ans.lower().startswith('y'):
+ continue
+ ans = input("What would you like to set it to? >> ")
+ setattr(person, att, ans)
+
+
+if __name__ == "__main__":
+ # a little test code:
+
+ # create a couple people:
+ p1 = Person("Fred", "Jones", "206-555-1234")
+ update_person(p1)
+
+
diff --git a/_downloads/hello_unicode.py b/_downloads/hello_unicode.py
new file mode 100644
index 00000000..6bbad1de
--- /dev/null
+++ b/_downloads/hello_unicode.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+hello = 'Hello '
+world = u'世界'
+
+print hello + world
+
+print u"It was nice weather today: it reached 80\u00B0"
+
+print u"Maybe it will reach 90\N{degree sign}"
+
+print u"It is extremely rare for it ever to reach 100° in Seattle"
diff --git a/_downloads/html_render.py b/_downloads/html_render.py
new file mode 100644
index 00000000..508400f4
--- /dev/null
+++ b/_downloads/html_render.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python3
+
+"""
+A class-based system for rendering html.
+"""
+
+
+# This is the framework for the base class
+class Element(object):
+
+ def __init__(self, content=None):
+ pass
+
+ def append(self, new_content):
+ pass
+
+ def render(self, out_file):
+ out_file.write("just something as a place holder...")
diff --git a/_downloads/index_slicing.py b/_downloads/index_slicing.py
new file mode 100644
index 00000000..dedd464c
--- /dev/null
+++ b/_downloads/index_slicing.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+
+"""
+examples / test code for __getindex__
+
+Doesn't really do anything, but you can see what happens with different indexing.
+"""
+
+import operator
+
+
+class IndexTest:
+
+ def __getitem__(self, index):
+ # print("In getindex, indexes is:", index)
+ if isinstance(index, slice):
+ print("it's a single slice:", index)
+ elif isinstance(index, tuple):
+ print("it's a multi-dimensional slice:", index)
+ else:
+ try:
+ ind = operator.index(index) # this converts arbitrary objects to an int.
+ print("it's an index: ", ind)
+ except TypeError: # not a simple index
+ raise
+ print("It's a simple index")
+
+
+if __name__ == "__main__":
+
+ it = IndexTest()
+
+ print("calling with simple index")
+ it[4]
+
+ print("calling with single slice")
+ it[3:4]
+
+ print("calling with two slices")
+ it[3:4, 7:8]
+
+ print("calling with an invalid index")
+ it["this"]
+
+
+
+
+
+
+
diff --git a/_downloads/integrate.py b/_downloads/integrate.py
new file mode 100644
index 00000000..f96aadd8
--- /dev/null
+++ b/_downloads/integrate.py
@@ -0,0 +1,44 @@
+def f(x):
+ return x**2
+
+
+def integrate(f, a, b, N):
+ s = 0
+ dx = (b - a) / N
+ for i in range(N):
+ s += f(a + i * dx)
+ return s * dx
+
+
+def integrate_f_with_functional_tools(f, a, b, N):
+ dx = (b - a) / N
+ return sum(map(f, ((a + y * dx) for y in range(N)))) * dx
+
+
+
+
+
+
+
+
+
+
+
+
+# imported here so the rest of the code can run without it
+import numpy as np
+
+def integrate_numpy(f, a, b, N):
+ """
+ numpy can be used to "vectorize" the problem
+
+ f must be "numpy comaptible"
+
+ """
+ dx = (b - a) / N
+ i = np.arange(N)
+ s = np.sum(f(a + (i * dx)))
+ return s * dx
+
+
+
diff --git a/_downloads/integrate_threads.py b/_downloads/integrate_threads.py
new file mode 100644
index 00000000..16c0d7f0
--- /dev/null
+++ b/_downloads/integrate_threads.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+
+import threading
+import queue
+
+# from integrate.integrate import integrate, f
+from integrate import f, integrate_numpy as integrate
+from decorators import timer
+
+
+@timer
+def threading_integrate(f, a, b, N, thread_count=2):
+ """break work into N chunks"""
+ N_chunk = int(float(N) / thread_count)
+ dx = float(b - a) / thread_count
+
+ results = queue.Queue()
+
+ def worker(*args):
+ results.put(integrate(*args))
+
+ for i in range(thread_count):
+ x0 = dx * i
+ x1 = x0 + dx
+ thread = threading.Thread(target=worker, args=(f, x0, x1, N_chunk))
+ thread.start()
+ print("Thread %s started" % thread.name)
+
+ return sum((results.get() for i in range(thread_count)))
+
+
+if __name__ == "__main__":
+
+ # parameters of the integration
+ a = 0.0
+ b = 10.0
+ N = 10**8
+ thread_count = 1
+
+ print("Numerical solution with N=%(N)d : %(x)f" %
+ {'N': N, 'x': threading_integrate(f, a, b, N, thread_count=thread_count)})
+
diff --git a/_downloads/iterator_1.py b/_downloads/iterator_1.py
new file mode 100644
index 00000000..479e5bf7
--- /dev/null
+++ b/_downloads/iterator_1.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+
+"""
+Simple iterator examples
+"""
+
+
+class IterateMe_1:
+ """
+ About as simple an iterator as you can get:
+
+ returns the sequence of numbers from zero to 4
+ ( like range(4) )
+ """
+
+ def __init__(self, stop=5):
+ self.current = -1
+ self.stop = stop
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ self.current += 1
+ if self.current < self.stop:
+ return self.current
+ else:
+ raise StopIteration
+
+
+if __name__ == "__main__":
+
+ print("Testing the iterator")
+ for i in IterateMe_1():
+ print(i)
diff --git a/_downloads/json_save.zip b/_downloads/json_save.zip
new file mode 100644
index 00000000..86b3357a
Binary files /dev/null and b/_downloads/json_save.zip differ
diff --git a/_downloads/latin1_test.py b/_downloads/latin1_test.py
new file mode 100644
index 00000000..3990078f
--- /dev/null
+++ b/_downloads/latin1_test.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+"""
+An example of using latin-1 as a universal encoding
+
+latin-1 is a superset of ASCII that is suitable for western european languages.
+
+Is the most common, and a good default, if you need a one-byte per char encoding
+for European text.
+
+It also has a nice property:
+ : every byte value from 0 to 255 is avalid charactor
+
+Thus you will never get an UnicodeDecodeError if
+you try to decode arbitrary bytes with latin-1.
+
+And it can "round-trip" trhough a unicode object.
+
+This can be useful is you don't know the encoding -- at least it won't break.
+It's also useful if you need to work with cobined text+binary data.
+
+
+
+"""
+
+# all the byte values in a bytes (str) object:
+all_bytes = ''.join( [chr(i) for i in range(255)] )
+
+print type(all_bytes)
+print len(all_bytes)
+
+print "Example value: 20"
+print ord(all_bytes[20]) == 20
+print "Example high value: 245"
+print ord(all_bytes[245]) == 245
+
+# now decode it to a unicode object:
+try:
+ uni = all_bytes.decode()
+except UnicodeDecodeError:
+ print "OOPS: can't decode with default encoding"
+
+# latin-1 works:
+try:
+ all_uni = all_bytes.decode('latin-1')
+ print "Yup -- that worked"
+ print all_uni
+ print "note that the ASCII subset is the same..."
+except UnicodeDecodeError:
+ print "OOPS: This should have worked!!"
+ raise
+
+## now show that it round-trips:
+all_bytes2 = all_uni.encode('latin-1')
+
+if all_bytes2 == all_bytes:
+ print "yup -- that worked...the values are preserved on the round trip."
+else:
+ print "Hey, that should have worked"
+
+
+
+
+
+
+
diff --git a/_downloads/listing1.py b/_downloads/listing1.py
new file mode 100644
index 00000000..e6f10147
--- /dev/null
+++ b/_downloads/listing1.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+# coding: utf-8
+"""
+"""
+
+import string
+import string
+
+
+module_variable = 0
+
+float = 1.0
+
+long = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"
+
+
+
+
+
+def functionName(self, int):
+ local = 5 + 5
+ module_variable = 5*5
+ return module_variable
+
+class my_class(object):
+
+ def __init__(self, arg1, string):
+ self.value = True
+ return
+
+ def method1(self, str):
+ self.s = str
+ return self.value
+
+ def method2(self):
+ return
+ print('How did we get here?')
+
+ def method1(self):
+ return self.value + 1
+ method2 = method1
+
+class my_subclass(my_class):
+
+ def __init__(self, arg1, string):
+ self.value = arg1
+ return
+
+
+
+class Food(object):
+ pass
+
+class Pizza(Food):
+ pass
+
+# test recommendations from http://legacy.python.org/dev/peps/pep-0008/#programming-recommendations
+
+# http://legacy.python.org/dev/peps/pep-0008/#constants
+food = Food()
+pizza = Pizza()
+
+print(type(food) == type(pizza))
+print(isinstance(food, Food))
+print(isinstance(pizza, Food))
+
+# create a larger Cyclomatic complexity, error triggered with
+# flake8 --max-complexity=5
+def f(x):
+ if x is 1:
+ return x
+ elif x is 2:
+ return x
+ elif x is 3:
+ return x
+ elif x is 4:
+ return x
+ elif x is 5:
+ return x
+
+print(f(5))
diff --git a/_downloads/lock_exercise.zip b/_downloads/lock_exercise.zip
new file mode 100644
index 00000000..d315044c
Binary files /dev/null and b/_downloads/lock_exercise.zip differ
diff --git a/_downloads/mangler.py b/_downloads/mangler.py
new file mode 100644
index 00000000..a8e22067
--- /dev/null
+++ b/_downloads/mangler.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+
+"""
+Simple metaclass example that creates upper and lower case versions of
+all non-dunder class attributes
+"""
+
+
+class NameMangler(type): # deriving from type makes it a metaclass.
+
+ def __new__(cls, clsname, bases, _dict):
+ uppercase_attr = {}
+ for name, val in _dict.items():
+ if not name.startswith('__'):
+ uppercase_attr[name.upper()] = val
+ uppercase_attr[name.lower()] = val
+ else:
+ uppercase_attr[name] = val
+
+ return super().__new__(cls, clsname, bases, uppercase_attr)
+
+
+class Foo(metaclass=NameMangler):
+ x = 1
+ Y = 2
+
+
+# note that it works for methods, too!
+class Bar(metaclass=NameMangler):
+ x = 1
+
+ def a_method(self):
+ print("in a_method")
+
+
+if __name__ == "__main__":
+ f = Foo()
+ print(f.x)
+ print(f.X)
+ print(f.y)
+ print(f.Y)
+
+ b = Bar()
+ b.A_METHOD()
diff --git a/_downloads/mangler_dec.py b/_downloads/mangler_dec.py
new file mode 100644
index 00000000..e8ca4938
--- /dev/null
+++ b/_downloads/mangler_dec.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+
+"""
+class decorator that adds both upper and lower case versions of
+class attributes.
+
+Same as the NameMangler metaclass, but with a class decorator instead
+
+Usage example:
+
+@name_mangler
+class Foo:
+ x = 1
+
+f = Foo()
+print(f.x)
+print(f.X)
+"""
+
+
+def name_mangler(cls):
+ """
+ Class decorator that adds upper and lower case names to the
+ decorated class
+ """
+ # get the dictionary of class attributes
+ att_dict = vars(cls)
+ # create a new dict to hold the attributes
+ new_attrs = {}
+ # loop thorough all the class attributes
+ for name, val in att_dict.items():
+ # skip all the "dunder" attributes
+ if not name.startswith("__"):
+ # Create both upper and lower case versions of all non-dunder names
+ # They are stored in the new_attrs dict, as you can't
+ # update the class namespace while looping through it.
+ new_attrs[name.upper()] = val
+ new_attrs[name.lower()] = val
+ # Add the new names to the cls attributes
+ # you can't directly update the __dict__ -- class __dict__s are not
+ # writable.
+ for name, val in new_attrs.items():
+ setattr(cls, name, val)
+ return cls
+
+
+@name_mangler
+class Foo:
+ x = 1
+ Y = 2
+
+
+# note that it works for methods, too!
+@name_mangler
+class Bar:
+ x = 1
+
+ def a_method(self):
+ print("in a_method")
+
+
+if __name__ == "__main__":
+ f = Foo()
+ print(f.x)
+ print(f.X)
+ print(f.y)
+ print(f.Y)
+
+ b = Bar()
+ b.A_METHOD()
+
diff --git a/_downloads/my_for.py b/_downloads/my_for.py
new file mode 100644
index 00000000..6023ad9f
--- /dev/null
+++ b/_downloads/my_for.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+
+"""
+hand writing 'for'
+
+demonstrates how for interacts with an iterable
+"""
+
+
+l = [1,2,3,4,5,]
+
+
+def my_for(an_iterable, func):
+ """
+ Emulation of a for loop.
+
+ func() will be called with each item in an_iterable
+
+ :param an_iterable: anything that satisfies the interation protocol
+
+ :param func: a callable -- it will be called, passing in each item
+ in an_iterable.
+
+ """
+ # equiv of "for i in l:"
+ iterator = iter(an_iterable)
+ while True:
+ try:
+ i = next(iterator)
+ except StopIteration:
+ break
+ func(i)
+
+
+if __name__ == "__main__":
+
+ def print_func(x):
+ print(x)
+
+ l = [1,2,3,4,5,]
+ my_for(l, print_func)
+
+ t = ('a','b','c','d')
+
+ my_for(t, print_func)
+
+
+
+
+
diff --git a/_downloads/nba_stats_async.py b/_downloads/nba_stats_async.py
new file mode 100644
index 00000000..78aacf89
--- /dev/null
+++ b/_downloads/nba_stats_async.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+
+"""
+Gathering statistics on NBA players asynchronously with the
+
+aiohttp library
+
+Running this on my machine, on my home network, took:
+
+
+***NOTE***
+On my OS-X box, a regular user is limited to 256 open files per process.
+A socket is considered a file -- so this can crash out when it hits that limit.
+
+(as of now, there are 491 players listed)
+
+You can increase it with:
+
+ulimit -n 2048
+
+And see what it's set to with:
+
+ulimit -a
+***********
+
+Borrowed from:
+
+http://terriblecode.com/blog/asynchronous-http-requests-in-python/
+"""
+import pdb
+import asyncio
+import aiohttp
+import json
+import time
+import requests
+
+base_url = 'http://stats.nba.com/stats'
+
+HEADERS = {
+ 'user-agent': ('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) '
+ 'AppleWebKit/537.36 (KHTML, like Gecko) '
+ 'Chrome/45.0.2454.101 Safari/537.36'),
+}
+
+# # this needs to be run first before we can start -- so no need for async
+# # but making it async so we can use the aiohttp lib.
+# async def get_players(players):
+# """
+# get the names of all the players we are interested in
+
+# This request will get JSON of the players for the 2016-17 season:
+
+# http://stats.nba.com/stats/commonallplayers?LeagueID=00&season=2016-17&isonlycurrentseason=1
+# """
+# endpoint = '/commonallplayers'
+# params = {'leagueid': '00', 'season': '2016-17', 'isonlycurrentseason': '1'}
+# url = base_url + endpoint
+# print('Getting all players...')
+# async with aiohttp.ClientSession() as session:
+# print("got the session")
+# async with session.get(url, headers=HEADERS, params=params) as resp:
+# print("got the response")
+# data = await resp.json()
+# players.append([(item[0], item[2]) for item in data['resultSets'][0]['rowSet']])
+
+
+def get_players(player_args):
+ """
+ get the names of all the players we are interested in
+
+ This request will get JSON of the players for the 2016-17 season:
+
+ http://stats.nba.com/stats/commonallplayers?LeagueID=00&season=2016-17&isonlycurrentseason=1
+
+ """
+ endpoint = '/commonallplayers'
+ params = {'leagueid': '00', 'season': '2016-17', 'isonlycurrentseason': '1'}
+ url = base_url + endpoint
+ print('Getting all players...')
+ print("about to make request")
+ resp = requests.get(url, headers=HEADERS, params=params)
+ print("got the response")
+ data = resp.json()
+ player_args.extend(
+ [(item[0], item[2]) for item in data['resultSets'][0]['rowSet']])
+
+
+# this is what we want to make concurrent
+async def get_player(player_id, player_name):
+ endpoint = '/commonplayerinfo'
+ params = {'playerid': player_id}
+ url = base_url + endpoint
+ print("Getting player", player_name)
+ async with aiohttp.ClientSession() as session:
+ print("session created")
+ async with session.get(url,
+ skip_auto_headers=["User-Agent"],
+ headers=HEADERS,
+ params=params) as resp:
+ print("response:", resp)
+ all_players[player_name] = await resp.json()
+ print("got:", player_name)
+ print("Done with get_player:", player_name)
+
+# async def get_all_stats(players):
+# for id, name in players:
+# print("getting:", name)
+# all_players[name] = await get_player(id, name)
+
+all_players = {}
+players = []
+
+start = time.time()
+loop = asyncio.get_event_loop()
+
+print("getting the players")
+# loop.run_until_complete(get_players(players))
+
+get_players(players)
+print("got the players")
+print("there are {} players".format(len(players)))
+
+# print("getting the stats")
+# loop.run_until_complete(get_all_stats(players[:200]))
+# print("got the stats")
+
+loop.run_until_complete(asyncio.gather(
+ *(get_player(*args) for args in players[:10])
+ )
+ )
+
+# loop.run_until_complete(get_player(*players[0]))
+
+# for id, name in players:
+# all_players[name] = get_player(id, name)
+
+print("Done getting data: it took {:.2F} seconds".format(time.time() - start))
+
+# write it out to a file
+with open("NBA_stats_2.json", 'w') as outfile:
+ json.dump(all_players, outfile, indent=2)
+
+print("File written out")
diff --git a/_downloads/nba_stats_sync.py b/_downloads/nba_stats_sync.py
new file mode 100644
index 00000000..5413814b
--- /dev/null
+++ b/_downloads/nba_stats_sync.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+
+"""
+Gathering statistics on NBA players with the regular old
+synchronous requests library.
+
+It took: 214.62 seconds (3.6 minutes) on my machine at home on May 29th
+
+Borrowed from:
+
+http://terriblecode.com/blog/asynchronous-http-requests-in-python/
+"""
+
+import requests
+import json
+import time
+
+base_url = 'http://stats.nba.com/stats'
+HEADERS = {
+ 'user-agent': ('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) '
+ 'AppleWebKit/537.36 (KHTML, like Gecko) '
+ 'Chrome/45.0.2454.101 Safari/537.36'),
+}
+
+
+def get_players(player_args):
+ """
+ get the names of all the players we are interested in
+
+ This request will get JSON of the players for the 2016-17 season:
+
+ http://stats.nba.com/stats/commonallplayers?LeagueID=00&season=2016-17&isonlycurrentseason=1
+
+ """
+ endpoint = '/commonallplayers'
+ params = {'leagueid': '00',
+ 'season': '2016-17',
+ 'isonlycurrentseason': '1'}
+ url = base_url + endpoint
+ print('Getting all players...')
+ resp = requests.get(url,
+ headers=HEADERS,
+ params=params)
+ data = resp.json()
+ player_args.extend(
+ [(item[0], item[2]) for item in data['resultSets'][0]['rowSet']])
+
+
+def get_player(player_id, player_name):
+ """
+ The request for a player's stats.
+
+ Should be a request like:
+
+ http://stats.nba.com/stats/commonplayerinfo?playerid=203112
+ """
+ endpoint = '/commonplayerinfo'
+ params = {'playerid': player_id}
+ url = base_url + endpoint
+ print("Getting player", player_name, player_id)
+ resp = requests.get(url,
+ headers=HEADERS,
+ params=params)
+ print(resp)
+ data = resp.json()
+ all_players[player_name] = data
+
+all_players = {}
+players = []
+
+start = time.time()
+get_players(players)
+
+print("there are {} players".format(len(players)))
+for id, name in players:
+ get_player(id, name)
+
+print("Done getting data: it took {:.2F} seconds".format(time.time() - start))
+
+# write it out to a file
+with open("NBA_stats.json", 'w') as outfile:
+ json.dump(all_players, outfile, indent=2)
+
+print("File written out")
+
diff --git a/_downloads/neo4j-developer-manual-3.3-python.pdf b/_downloads/neo4j-developer-manual-3.3-python.pdf
new file mode 100644
index 00000000..7b0767eb
Binary files /dev/null and b/_downloads/neo4j-developer-manual-3.3-python.pdf differ
diff --git a/_downloads/object_canvas.py b/_downloads/object_canvas.py
new file mode 100644
index 00000000..8c28631f
--- /dev/null
+++ b/_downloads/object_canvas.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+
+"""
+object canvas: an example of multiple inheritance and mix-ins
+
+This is a simplified version of FloatCanvas -- an extension to the
+wxPython desktop GUI library
+
+FloatCanvas is a system for handling zoomable and scalable graphics in
+a object-persistant way. That is, graphic objects like circles and
+rectangles and what not can be created ahead of time, and then the Canvas
+can render them accoding to the current zoom level and pan position, etc.
+
+This lets the user think about their graphics object should look like,
+and not have to worry about exactly how to draw them -- or their pixel
+coordinates, or anything else.
+
+If you want to see all this in all its full complexity, the FloatCanvas
+code in part of the wxPython project, and can be seen here:
+
+https://github.com/wxWidgets/Phoenix/tree/master/wx/lib/floatcanvas
+
+This code: object_canvas is a simplified version. It doesn't allow scaling
+or zooming, and only renders in pixel coordinates. But it does allow
+object-persistance, and is a nice demo of the use of mixins.
+
+This version requires the Python Imaging Library to do the rendering.
+
+You can get it by installing the "pillow" package from PyPi:
+
+python -m pip install pillow
+
+Its docs are here:
+
+https://pillow.readthedocs.io/en/4.3.x/index.html
+
+"""
+from math import ceil
+
+from PIL import Image, ImageDraw
+
+
+class ObjectCanvas():
+ """
+ An object-oriented canvas for drawing things
+ """
+
+ def __init__(self,
+ size=(500, 500),
+ background=(255, 255, 255, 0)
+ ):
+ self.size = size
+ self.draw_objects = []
+ self.background = background
+
+ def add_object(self, draw_object, position="top"):
+ #fixme: maybe overload the inplace addition operator?
+ """
+ Add a new object to the canvas.
+
+ :param: draw_object -- DrawObject to add
+
+ :param position="top": Position to add the object. "top" puts
+ the object on top of teh other objects.
+ "bottom" puts them on the bottom of the stack.
+ A integer puts it in that place in the order
+ -- 0 is the bottom.
+ """
+ if position == "top":
+ self.draw_objects.append(draw_object)
+ elif position == "bottom":
+ self.draw_objects.insert(0, draw_object)
+ else:
+ self.draw_objects.insert(position, draw_object)
+
+ def render(self, filename):
+ """
+ render the drawing to a file with the given name
+ """
+ image = Image.new('RGBA', self.size, color=self.background)
+ drawer = ImageDraw.Draw(image)
+
+ for do in self.draw_objects:
+ do.draw(drawer)
+ image.save(filename)
+
+
+class DrawObject:
+ """
+ base class for all draw objects
+ """
+
+ def __init__(self, *args, **kwargs):
+ print("in DrawObject __init__", kwargs)
+ # do nothing, but to make super happy
+ super().__init__(*args, **kwargs)
+
+
+class LineObject:
+ """
+ mixin for classes with a line
+ """
+
+ def __init__(self,
+ line_color='black',
+ line_width=1,
+ **kwargs,
+ ):
+ print("in LineObject __init__", kwargs)
+ super().__init__(**kwargs)
+ self.line_color = line_color
+ self.line_width = line_width
+
+
+class FillObject:
+ """
+ mixin for classes with a fill
+ """
+
+ def __init__(self,
+ fill_color=None,
+ **kwargs
+ ):
+ print("in FillObject __init__", kwargs)
+ self.fill_color = fill_color
+
+
+class PolyLine(DrawObject, LineObject):
+ def __init__(self,
+ vertices,
+ **kwargs
+ ):
+ self.vertices = vertices
+ print("in PolyLine init", kwargs)
+ super().__init__(**kwargs)
+
+ def draw(self, drawer):
+ """
+ draw the object
+
+ :param drawer: PIL.ImageDraw object to draw to
+ """
+ drawer.line(self.vertices, fill=self.line_color, width=self.line_width)
+
+
+class Circle(DrawObject, LineObject, FillObject):
+ def __init__(self, center, diameter, **kwargs):
+ self.center = center
+ self.diameter = diameter
+ super().__init__(**kwargs)
+
+ def draw(self, drawer):
+ """
+ Draw the object
+ :param drawer: PIL.ImageDraw object to draw to
+ """
+ r = self.diameter // 2
+ c = self.center
+ # PIL doesn't support different line widths for ellipses,
+ # so we fake it.
+ lw2 = self.line_width / 2
+ bounds = ((c[0] - r, c[1] - r), (c[0] + r, c[1] + r))
+ drawer.ellipse(bounds, fill=self.fill_color, outline=None)
+ for i in range(int(ceil(lw2)), int(-lw2), -1):
+ r = self.diameter // 2 + i
+ bounds = ((c[0] - r, c[1] - r), (c[0] + r, c[1] + r))
+ drawer.ellipse(bounds, fill=None, outline=self.line_color)
+
+
+
+
+
+
diff --git a/_downloads/play_with_imports.py b/_downloads/play_with_imports.py
new file mode 100644
index 00000000..deaaf375
--- /dev/null
+++ b/_downloads/play_with_imports.py
@@ -0,0 +1,26 @@
+
+print('importing')
+
+
+def my_decorator(func):
+ print('in my_decorator for: ', func.__name__)
+ def inner():
+ print('in inner function')
+ func()
+ print('in inner after decorated function')
+ return inner
+
+
+@my_decorator
+def first_func():
+ print('running first_func')
+
+@my_decorator
+def other_func():
+ print('running other_func')
+
+print('imports and loading done')
+
+if __name__ == '__main__':
+ print('run script')
+ other_func()
diff --git a/_downloads/play_with_scope.py b/_downloads/play_with_scope.py
new file mode 100644
index 00000000..a71292be
--- /dev/null
+++ b/_downloads/play_with_scope.py
@@ -0,0 +1,47 @@
+"""
+some example code to play with scope
+"""
+
+def start_at(x):
+ def increment_by(y):
+ return x + y
+ return increment_by
+
+closure_1 = start_at(3)
+closure_2 = start_at(5)
+closure_1(2)
+start_at(2)(4)
+
+
+def make_um_counter():
+ series = []
+ def um_counter(new_word):
+ series.append(new_word) # free variable
+ count = 0
+ for i in series:
+ if i == 'um':
+ count += 1
+ return count
+ return um_counter
+
+
+def make_um_counter2():
+ count = 0
+
+ def um_counter2(new_word):
+ if new_word == 'um':
+ count += 1
+ return count
+ return um_counter2
+
+
+# try using nonlocal:
+def make_um_counter3():
+ count = 0
+
+ def um_counter3(new_word):
+ nonlocal count
+ if new_word == 'um':
+ count += 1
+ return count
+ return um_counter3
diff --git a/_downloads/properties_example.py b/_downloads/properties_example.py
new file mode 100644
index 00000000..f70760e9
--- /dev/null
+++ b/_downloads/properties_example.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+
+"""
+Example code for properties
+
+NOTE: if your getters and setters are this simple: don't do this!
+
+"""
+
+
+class C:
+ def __init__(self):
+ self._x = None
+ @property
+ def x(self):
+ print("in getter")
+ return self._x
+ @x.setter
+ def x(self, value):
+ print("in setter", value)
+ self._x = value
+ @x.deleter
+ def x(self):
+ del self._x
+
+if __name__ == "__main__":
+ c = C()
+ c.x = 5
+ print(c.x)
+
diff --git a/_downloads/property_ugly.py b/_downloads/property_ugly.py
new file mode 100644
index 00000000..fe65f35e
--- /dev/null
+++ b/_downloads/property_ugly.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+class C:
+ """
+ Property defined in about the most ugly way possible
+ """
+ def __init__(self):
+ self._x = None
+ def x(self):
+ return self._x
+ x = property(x)
+ def _set_x(self, value):
+ self._x = value
+ x = x.setter(_set_x)
+ def _del_x(self):
+ del self._x
+ x = x.deleter(_del_x)
+
+
diff --git a/_downloads/pytest_fixtures.py b/_downloads/pytest_fixtures.py
new file mode 100644
index 00000000..36d74349
--- /dev/null
+++ b/_downloads/pytest_fixtures.py
@@ -0,0 +1,60 @@
+#!usr/bin/env python
+
+"""
+example to show how fixtures work in pytest
+
+to run this and see the output:
+
+py.test -s -v pytest_fixtures.py
+"""
+
+import pytest
+
+
+@pytest.fixture
+# with module-level scope
+#@pytest.fixture(scope="module")
+def example_fixture():
+ """
+ An example fixture that does nothing useful
+
+ But does return an object you can use for testing
+ """
+ print("I am running the fixture now")
+ return {"this": 3,
+ "that": 2}
+
+
+# now use the fixture in a couple tests
+def test_one(example_fixture):
+ print("running test_one")
+ assert example_fixture["this"] == 3
+
+
+def test_two(example_fixture):
+ print("running test_two")
+ assert example_fixture["that"] == 2
+
+# with teardown:
+@pytest.fixture(scope="module")
+def example_fixture2():
+ """
+ An example fixture that does nothing useful
+
+ But does return an object you can use for testing
+ """
+ print("I am running the fixture now")
+ yield {"this": 3,
+ "that": 2}
+ print("and now I am running the teardown code")
+
+
+# using the fixture with teardown:
+def test_three(example_fixture2):
+ print("running test_three")
+ assert example_fixture2["this"] == 3
+
+
+def test_four(example_fixture2):
+ print("running test_four")
+ assert example_fixture2["this"] == 3
diff --git a/_downloads/quadratic.py b/_downloads/quadratic.py
new file mode 100644
index 00000000..4b782230
--- /dev/null
+++ b/_downloads/quadratic.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python3
+
+"""
+A quaratic function evaluator
+
+used to demonstrate callable classes
+"""
+
+class Quadratic:
+ """
+ Class to evaluate quadratic equations
+
+ Each instance wil have a certain set of coefficients
+ """
+
+ def __init__(self, A, B, C):
+ self.A = A
+ self.B = B
+ self.C = C
+
+ def __call__(self, x):
+ return self.A * x**2 + self.B * x + self.C
+
\ No newline at end of file
diff --git a/_downloads/race_condition.py b/_downloads/race_condition.py
new file mode 100644
index 00000000..49434eb1
--- /dev/null
+++ b/_downloads/race_condition.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+
+import threading
+import time
+
+
+# create a mutable object that is shared among threads
+class shared:
+ val = 1
+
+
+def func():
+ y = shared.val
+ time.sleep(0.00001)
+ y += 1
+ shared.val = y
+
+
+threads = []
+# with enough threads, there's sufficient overhead to
+# cause a race condition
+for i in range(100):
+ thread = threading.Thread(target=func)
+ threads.append(thread)
+ thread.start()
+
+for thread in threads:
+ thread.join()
+
+print(shared.val)
+
diff --git a/_downloads/raising_an_assert.py b/_downloads/raising_an_assert.py
new file mode 100644
index 00000000..0ea86ea5
--- /dev/null
+++ b/_downloads/raising_an_assert.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+"""
+examples of forcing and an AssertionError
+"""
+
+
+def test_raise_assertion():
+ raise AssertionError("this was done with a direct raise")
+
+
+def test_trasditional_assert():
+ assert False, "this was done with a forced assert"
diff --git a/_downloads/run_html_render.py b/_downloads/run_html_render.py
new file mode 100644
index 00000000..9608a65f
--- /dev/null
+++ b/_downloads/run_html_render.py
@@ -0,0 +1,231 @@
+#!/usr/bin/env python3
+
+"""
+a simple script can run and test your html rendering classes.
+
+Uncomment the steps as you add to your rendering.
+
+"""
+
+from io import StringIO
+
+# importing the html_rendering code with a short name for easy typing.
+import html_render as hr
+
+
+# writing the file out:
+def render_page(page, filename, indent=None):
+ """
+ render the tree of elements
+
+ This uses StringIO to render to memory, then dump to console and
+ write to file -- very handy!
+ """
+
+ f = StringIO()
+ if indent is None:
+ page.render(f)
+ else:
+ page.render(f, indent)
+
+ print(f.getvalue())
+ with open(filename, 'w') as outfile:
+ outfile.write(f.getvalue())
+
+
+# Step 1
+#########
+
+page = hr.Element()
+
+page.append("Here is a paragraph of text -- there could be more of them, "
+ "but this is enough to show that we can do some text")
+
+page.append("And here is another piece of text -- you should be able to add any number")
+
+render_page(page, "test_html_output1.html")
+
+# The rest of the steps have been commented out.
+# Uncomment them as you move along with the assignment.
+
+# ## Step 2
+# ##########
+
+# page = hr.Html()
+
+# body = hr.Body()
+
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text"))
+
+# body.append(hr.P("And here is another piece of text -- you should be able to add any number"))
+
+# page.append(body)
+
+# render_page(page, "test_html_output2.html")
+
+# # Step 3
+# ##########
+
+# page = hr.Html()
+
+# head = hr.Head()
+# head.append(hr.Title("PythonClass = Revision 1087:"))
+
+# page.append(head)
+
+# body = hr.Body()
+
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text"))
+# body.append(hr.P("And here is another piece of text -- you should be able to add any number"))
+
+# page.append(body)
+
+# render_page(page, "test_html_output3.html")
+
+# # Step 4
+# ##########
+
+# page = hr.Html()
+
+# head = hr.Head()
+# head.append(hr.Title("PythonClass = Revision 1087:"))
+
+# page.append(head)
+
+# body = hr.Body()
+
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text",
+# style="text-align: center; font-style: oblique;"))
+
+# page.append(body)
+
+# render_page(page, "test_html_output4.html")
+
+# # Step 5
+# #########
+
+# page = hr.Html()
+
+# head = hr.Head()
+# head.append(hr.Title("PythonClass = Revision 1087:"))
+
+# page.append(head)
+
+# body = hr.Body()
+
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text",
+# style="text-align: center; font-style: oblique;"))
+
+# body.append(hr.Hr())
+
+# page.append(body)
+
+# render_page(page, "test_html_output5.html")
+
+# # Step 6
+# #########
+
+# page = hr.Html()
+
+# head = hr.Head()
+# head.append(hr.Title("PythonClass = Revision 1087:"))
+
+# page.append(head)
+
+# body = hr.Body()
+
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text",
+# style="text-align: center; font-style: oblique;"))
+
+# body.append(hr.Hr())
+
+# body.append("And this is a ")
+# body.append( hr.A("http://google.com", "link") )
+# body.append("to google")
+
+# page.append(body)
+
+# render_page(page, "test_html_output6.html")
+
+# # Step 7
+# #########
+
+# page = hr.Html()
+
+# head = hr.Head()
+# head.append(hr.Title("PythonClass = Revision 1087:"))
+
+# page.append(head)
+
+# body = hr.Body()
+
+# body.append( hr.H(2, "PythonClass - Class 6 example") )
+
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text",
+# style="text-align: center; font-style: oblique;"))
+
+# body.append(hr.Hr())
+
+# list = hr.Ul(id="TheList", style="line-height:200%")
+
+# list.append( hr.Li("The first item in a list") )
+# list.append( hr.Li("This is the second item", style="color: red") )
+
+# item = hr.Li()
+# item.append("And this is a ")
+# item.append( hr.A("http://google.com", "link") )
+# item.append("to google")
+
+# list.append(item)
+
+# body.append(list)
+
+# page.append(body)
+
+# render_page(page, "test_html_output7.html")
+
+# # Step 8 and 9
+# ##############
+
+# page = hr.Html()
+
+
+# head = hr.Head()
+# head.append( hr.Meta(charset="UTF-8") )
+# head.append(hr.Title("PythonClass = Revision 1087:"))
+
+# page.append(head)
+
+# body = hr.Body()
+
+# body.append( hr.H(2, "PythonClass - Example") )
+
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text",
+# style="text-align: center; font-style: oblique;"))
+
+# body.append(hr.Hr())
+
+# list = hr.Ul(id="TheList", style="line-height:200%")
+
+# list.append( hr.Li("The first item in a list") )
+# list.append( hr.Li("This is the second item", style="color: red") )
+
+# item = hr.Li()
+# item.append("And this is a ")
+# item.append( hr.A("http://google.com", "link") )
+# item.append("to google")
+
+# list.append(item)
+
+# body.append(list)
+
+# page.append(body)
+
+# render_page(page, "test_html_output8.html")
diff --git a/_downloads/sample_html.html b/_downloads/sample_html.html
new file mode 100644
index 00000000..9c2e675d
--- /dev/null
+++ b/_downloads/sample_html.html
@@ -0,0 +1,27 @@
+
+
+
+
+ Python Class Sample page
+
+
+ Python Class - Html rendering example
+
+ Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
+
+
+ -
+ The first item in a list
+
+ -
+ This is the second item
+
+ -
+ And this is a
+ link
+ to google
+
+
+
+
\ No newline at end of file
diff --git a/_downloads/series_template.py b/_downloads/series_template.py
new file mode 100644
index 00000000..fb161d70
--- /dev/null
+++ b/_downloads/series_template.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+
+"""
+a template for the series assignment
+"""
+
+
+def fibonacci(n):
+ """ compute the nth Fibonacci number """
+ pass
+
+
+def lucas(n):
+ """ compute the nth Lucas number """
+ pass
+
+
+def sum_series(n, n0=0, n1=1):
+ """
+ compute the nth value of a summation series.
+
+ :param n0=0: value of zeroth element in the series
+ :param n1=1: value of first element in the series
+
+ This function should generalize the fibonacci() and the lucas(),
+ so that this function works for any first two numbers for a sum series.
+ Once generalized that way, sum_series(n, 0, 1) should be equivalent to fibonacci(n).
+ And sum_series(n, 2, 1) should be equivalent to lucas(n).
+
+ sum_series(n, 3, 2) should generate antoehr series with no specific name
+
+ The defaults are set to 0, 1, so if you don't pass in any values, you'll
+ get the fibonacci sercies
+ """
+ pass
+
+if __name__ == "__main__":
+ # run some tests
+ assert fibonacci(0) == 0
+ assert fibonacci(1) == 1
+ assert fibonacci(2) == 1
+ assert fibonacci(3) == 2
+ assert fibonacci(4) == 3
+ assert fibonacci(5) == 5
+ assert fibonacci(6) == 8
+ assert fibonacci(7) == 13
+
+ assert lucas(0) == 2
+ assert lucas(1) == 1
+
+ assert lucas(4) == 7
+
+ # test that sum_series matches fibonacci
+ assert sum_series(5) == fibonacci(5)
+ assert sum_series(7, 0, 1) == fibonacci(7)
+
+ # test if sum_series matched lucas
+ assert sum_series(5, 2, 1) == lucas(5)
+
+ # test if sum_series works for arbitrary initial values
+ assert sum_series(0, 3, 2) == 3
+ assert sum_series(1, 3, 2) == 2
+ assert sum_series(2, 3, 2) == 5
+ assert sum_series(3, 3, 2) == 7
+ assert sum_series(4, 3, 2) == 12
+ assert sum_series(5, 3, 2) == 19
+
+ print("tests passed")
diff --git a/source/exercises/sherlock.txt b/_downloads/sherlock.txt
similarity index 100%
rename from source/exercises/sherlock.txt
rename to _downloads/sherlock.txt
diff --git a/source/exercises/sherlock_small.txt b/_downloads/sherlock_small.txt
similarity index 100%
rename from source/exercises/sherlock_small.txt
rename to _downloads/sherlock_small.txt
diff --git a/_downloads/simple_classes.py b/_downloads/simple_classes.py
new file mode 100644
index 00000000..4f996061
--- /dev/null
+++ b/_downloads/simple_classes.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+"""
+simple_classes.py
+
+demonstrating the basics of a class
+"""
+
+import math
+
+
+# create a point class
+class Point:
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+
+# create an instance of that class
+p = Point(3, 4)
+
+# access the attributes
+print("p.x is:", p.x)
+print("p.y is:", p.y)
+
+
+class Point2:
+ size = 4
+ color = "red"
+
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+
+p2 = Point2(4, 5)
+print(p2.size)
+print(p2.color)
+
+
+class Point3:
+ size = 4
+ color = "red"
+
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+
+ def get_color(self):
+ return self.color
+
+ def get_size(self):
+ return self.size
+
+class Rect:
+
+ def __init__(self, w, h):
+ self.w = w
+ self.h = h
+
+ def get_size(self):
+ return self.w * self.h
+
+
+p3 = Point3(4, 5)
+print(p3.size)
+print(p3.get_color())
+
+
+class Circle:
+ color = "red"
+ styles = ['dashed']
+
+ def __init__(self, diameter):
+ self.diameter = diameter
+
+ def grow(self, factor=2):
+ """
+ grows the circle's diameter
+
+ :param factor=2: factor by which to grow the circle
+ """
+ self.diameter = self.diameter * factor
+
+ def add_style(self, style):
+ self.styles.append(style)
+
+ def get_area(self):
+ return math.pi * self.diameter / 2.0
+
+
+class NewCircle(Circle):
+ color = "blue"
+
+ def grow(self, factor=2):
+ """grows the area by factor..."""
+ self.diameter = self.diameter * math.sqrt(2)
+
+nc = NewCircle
+print(nc.color)
+
+
+class CircleR(Circle):
+ def __init__(self, radius):
+ diameter = radius*2
+ Circle.__init__(self, diameter)
+
+
+class CircleR2(Circle):
+ def __init__(self, radius):
+ self.radius = radius
+
+ def get_area(self):
+ return Circle.get_area(self, self.radius*2)
diff --git a/_downloads/simple_timer.py b/_downloads/simple_timer.py
new file mode 100644
index 00000000..a7f4a9ed
--- /dev/null
+++ b/_downloads/simple_timer.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+
+"""
+Simple example of using a threading.Timer
+
+NOTE: The docstring is out of sync with the __init__!
+ I think it was inherited from the threading.Thread docstring.
+"""
+
+import threading
+import time
+import random
+
+
+def called_once():
+ """
+ this function is designed to be be called once in the future
+ """
+ print("Hey! I just got called! It's now: {}".format(time.asctime()))
+
+
+def called_later(count):
+ """
+ This function is designed to run, and then set up a timer to call
+ itself at a random time in the future
+
+ Note that it is limited to 10 invocations
+ Otherwise, there will always be a background
+ thread running that can not be easily killed
+ (at least on *nix -- Windows may let ^C kill it)
+
+ Try hitting ^C early in the run...
+ """
+
+ print("Hey! I just got called for {}th time! It's now: {}"
+ .format(count, time.asctime()))
+ # this can trigger another invocation
+ interval = random.randint(1, 3)
+ count += 1
+ if count < 10:
+ threading.Timer(interval=interval,
+ function=called_later,
+ args=(count,)).start()
+
+
+if __name__ == "__main__":
+ # use the timer...
+
+ # threading.Timer(interval=3, function=called_once).start()
+ # print("After starting the timer")
+ # print("it's now: {}".format(time.asctime()))
+
+ called_later(0)
+
+ # do some stuff:
+ for i in range(100):
+ print("{}: nothing important...".format(i))
+ time.sleep(0.5)
+
+
+
+
diff --git a/_downloads/singleton.py b/_downloads/singleton.py
new file mode 100644
index 00000000..890082b8
--- /dev/null
+++ b/_downloads/singleton.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+
+"""
+example of using __metaclass__ to impliment the singleton pattern
+"""
+
+
+class Singleton(type):
+ instance = None
+
+ def __call__(cls, *args, **kwargs):
+ if cls.instance is None:
+ cls.instance = super().__call__(*args, **kwargs)
+ return cls.instance
+
+
+class MyClass(metaclass=Singleton):
+ pass
+
+object1 = MyClass()
+object2 = MyClass()
+
+print(id(object1))
+print(id(object2))
diff --git a/_downloads/sort_key.py b/_downloads/sort_key.py
new file mode 100644
index 00000000..e10df5a7
--- /dev/null
+++ b/_downloads/sort_key.py
@@ -0,0 +1,55 @@
+"""
+demonstration of defining a sort_key method for sorting
+"""
+
+import random
+import time
+
+
+class Simple:
+ """
+ simple class to demonstrate a simple sorting key method
+ """
+
+ def __init__(self, val):
+ self.val = val
+
+ def sort_key(self):
+ """
+ sorting key function --used to pass in to sort functions
+ to get faster sorting
+
+ Example::
+
+ sorted(list_of_simple_objects, key=Simple.sort_key)
+
+ """
+ return self.val
+
+ def __lt__(self, other):
+ """
+ less than --required for regular sorting
+ """
+ return self.val < other.val
+
+ def __repr__(self):
+ return "Simple({})".format(self.val)
+
+
+if __name__ == "__main__":
+ N = 10000
+ a_list = [Simple(random.randint(0, 10000)) for i in range(N)]
+ # print("Before sorting:", a_list)
+
+ print("Timing for {} items".format(N))
+ start = time.clock()
+ sorted(a_list)
+ reg_time = time.clock() - start
+ print("regular sort took: {:.4g}s".format(reg_time))
+
+ start = time.clock()
+ sorted(a_list, key=Simple.sort_key)
+ key_time = time.clock() - start
+ print("key sort took: {:.4g}s".format(key_time))
+
+ print("performance improvement factor: {:.4f}".format((reg_time / key_time)))
diff --git a/_downloads/students.txt b/_downloads/students.txt
new file mode 100644
index 00000000..a85853a8
--- /dev/null
+++ b/_downloads/students.txt
@@ -0,0 +1,31 @@
+Name: Nickname, languages
+Swift, Taylor: python, java, perl
+Swift, Samuel: Sam
+Brooks, Garth: fortran, java, matlab, bash
+Buble, Michael: python, powershell
+Stefani, Gwen: c#, javascript, python, typescript
+Gaye, Marvin: c++, java
+Jagger, Michael: Mick, shell, python
+Lennon, John:
+Nelson, Willy: python, java
+McCartney, Paul: nothing
+Dylan, Bob: java, python, ruby
+Springsteen, Bruce: c++, python
+Jackson, Michael: java, c#, python
+Berry, Charles: Chuck c++, matlab,
+Wonder, Steven: Stevie, c, perl, java, erlang, python
+Nicks, Stevie: java, perl, c#, c++, python
+Turner, Tina: bash, python
+Plant, Robert: Bob, bash, python, ansible
+Townshend, Peter: Pete, c#, powershell, python, sql
+Moon, Keith: python, r, visualbasic
+Mitchell, Joni: php, mysql, python
+Ramone, John: Johnny, rex, db
+King, Carol: r
+Waters, Muddy: perl, python
+Star, Richard: Ringo, shell, python, vb
+Smith, Patricia: Patti, python
+Morrison, Jim: fortran, perl, sql, python
+Marley, Robert: Bob, c, c++, lisp
+Simon, Paul: bash, python, sql
+Charles, Ray: Chuck, java, python
diff --git a/_downloads/super_test.py b/_downloads/super_test.py
new file mode 100644
index 00000000..0a73e93f
--- /dev/null
+++ b/_downloads/super_test.py
@@ -0,0 +1,264 @@
+#!/usr/bin/env python3
+
+"""
+Some example code demonstrating some super() behaviour
+"""
+
+# Define a multiple inheritance scheme:
+class A():
+ def __init__(self):
+ print("in A __init__")
+ print("self's class is:", self.__class__)
+ s = super().__init__()
+
+
+class B():
+ def __init__(self):
+ print("in B.__init__")
+ s = super().__init__()
+
+
+class C():
+ def __init__(self):
+ print("in C.__init__")
+ s = super().__init__()
+
+
+class D(C, B, A):
+ def __init__(self):
+ print("self's class is:", self.__class__)
+ super().__init__()
+
+# print our D's method resoluton order
+# Is it what you expect?
+print("\nD's mro:")
+print( D.__mro__)
+# see what happens when you create a D object:
+print("\ninitializing a D object:")
+d = D()
+
+
+# ## super's parameters
+# To do its thing, super() needs to know two things:
+#
+# ``super(type, obj)``
+#
+# It needs to know that type (class) that you want the super-classes of,
+# AND it needs to know the actual object instance at the time it is called.
+#
+# python3 fills these in for you at run time, but in python2, you needed to
+# specify them:
+#
+# ```
+# class A(object):
+# def __init__(self):
+# super(A, self).__init__()
+# ```
+#
+# But why do you need BOTH `A` and `self`? -- isn't `self` an instance of `A`?
+#
+# Not neccesarily -- if A's method is being called from a subclass, then
+# `self` will be an instance of the subclass. `super()` requires that the object be an instance of the class (or a subclass).
+#
+# This distiction will come up later....
+#
+# Again, py3 takes care of this for you, though you CAN still spell it out.
+
+# see what you get with super by itself:
+s_c = super(C, d)
+print("\n the super object of super(C, d) itself")
+print(s_c)
+
+
+# This works because `d` is a `D` object, which is a subclass of `C`.
+
+# create a C instance:
+c = C()
+
+# and try to create a super with the missmatch
+print("\n the super object of super(D, c)")
+try:
+ super(D, c)
+except TypeError as err:
+ print(err)
+# But this gives a TypeError: `C` is NOT a subclass of `D`
+
+# it doesn't have to be an exact intance, jsust a subclass:
+
+print("\n the super object of super(A, d)")
+s_a = super(A, d)
+print(s_a)
+
+
+print("\n the super object of super(B, d)")
+s_b = super(B, d)
+print(s_b)
+
+print("\nD inherits from both A and B, so that worked...")
+
+print("\nD's MRO:")
+print(D.__mro__)
+
+
+# An Example of why you want to use super() everywhere.
+
+# Classes without super()
+
+class A():
+ def this(self):
+ print("in A.this")
+
+class B():
+ def this(self):
+ print("in B.this")
+
+class C(A,B):
+ def this(self):
+ print("in C.this")
+ A.this(self)
+ B.this(self)
+
+print("\nRunning without super()")
+print("Creating a C instance without super() -- and calling it's this method:")
+c = C()
+c.this()
+
+print("C's `this` explicitly called both A and B's methods -- so they all get called.")
+
+# Using super in just C:
+
+print("\n using super() in C, but not everywhere...")
+
+class A():
+ def this(self):
+ print("in A.this")
+
+class B(A):
+ def this(self):
+ print("in B.this")
+
+class C(B):
+ def this(self):
+ print("in C.this")
+ super().this()
+
+
+print("\n C's MRO")
+print(C.__mro__)
+
+print("\ncreating a C instance and calling it's this() method:")
+c = C()
+c.this()
+
+print("**NOTE: `A.this` did NOT get called!")
+
+# **Note:** `A.this` did NOT get called!
+#
+# Even though it is in in the MRO.
+#
+# Python stopped when it found the method in B.
+
+# ### Using super everywhere:
+
+print("using super everywhere:")
+
+class Base():
+ def this(self):
+ pass # just so there is a base that has the method
+
+class A(Base):
+ def this(self):
+ print("in A.this")
+ super().this()
+
+class B(Base):
+ def this(self):
+ print("in B.this")
+ super().this()
+class C(A,B):
+ def this(self):
+ print("in C.this")
+ super().this()
+
+
+print("\nnow create a C instance and call its this() method:")
+c = C()
+c.this()
+
+print("Now both A and B's methods get called -- probably what you want.")
+
+print("\nAnd the MRO of the base class")
+print(Base.__mro__)
+
+# But if you don't want both called -- better to just be Explicit, rather than use super():
+
+class Base():
+ def this(self):
+ pass # just so there is a base that has the method
+
+class A(Base):
+ def this(self):
+ print("in A.this")
+ super().this()
+
+class B(Base):
+ def this(self):
+ print("in B.this")
+ super().this()
+
+class C(A,B):
+ def this(self):
+ print("in C.this")
+ A.this(self)
+
+print("\nIf you want total control of what methods get called --don't use super"
+ "and be explicit")
+
+c = C()
+c.this()
+
+
+print("**Whoa** -- A and B's method DID get called! -- why?")
+
+print("A's MRO:")
+print(A.__mro__)
+
+
+print("B is not there")
+
+print("\nBut if we print the class of each instance when this() is called")
+
+class Base():
+ def this(self):
+ pass # just so there is a base that has the method
+
+class A(Base):
+ def this(self):
+ print("in A.this")
+ print("self's class in A's this method:", self.__class__)
+ super().this()
+
+class B(Base):
+ def this(self):
+ print("in B.this")
+ super().this()
+
+class C(A,B):
+ def this(self):
+ print("in C.this")
+ A.this(self)
+
+
+print("and create a c instance and call this():")
+c = C()
+c.this()
+
+print("In A's this method -- self is a C object")
+
+print("C's MRO:")
+print(C.__mro__)
+
+print("\nRemember, `super()` is dynamic -- what it calls is determined at run time.")
+print("That's how it knows to call ``B``'s method too.")
+print("Which is why we say that using `super()` is *part* of the interface of"
+ "the class.")
diff --git a/_downloads/test_address_book_mongo.py b/_downloads/test_address_book_mongo.py
new file mode 100644
index 00000000..83d714be
--- /dev/null
+++ b/_downloads/test_address_book_mongo.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+
+"""
+test code for the mongo specific addressbook model
+remember to start mongo database first
+
+$ mongod --dbpath=mongo_data/
+"""
+
+import pytest
+
+import address_book_mongo as model
+
+
+@pytest.fixture
+def chris():
+ chris = model.Person(last_name='Barker',
+ first_name='Chris',
+ middle_name='H',
+ cell_phone='(123) 555-7890',
+ email='PythonCHB@gmail.com',
+ )
+ return chris
+
+
+@pytest.fixture
+def fred():
+ fred = model.Person(last_name='Jones',
+ first_name='Fred',
+ middle_name='B',
+ cell_phone='(123) 555-7890',
+ email='FredJones@gmail.com',
+ )
+ return fred
+
+
+def test_person_to_dict(chris):
+ dct = chris.to_dict()
+
+ assert dct['last_name'] == "Barker"
+ assert dct['email'] == 'PythonCHB@gmail.com'
+
+
+def test_person_to_from_dict(chris):
+
+ dct = chris.to_dict()
+ chris2 = model.Person.from_dict(dct)
+
+ print(chris2)
+
+ assert chris2.last_name == 'Barker'
+ assert chris2.first_name == 'Chris'
+ assert chris2.middle_name == 'H'
+ assert chris2.cell_phone == '(123) 555-7890'
+ assert chris2.email == 'PythonCHB@gmail.com'
+
+
+def test_household_to_dict(chris):
+ home = model.Household(name="The Barkers",
+ people=(chris,),
+ address=model.Address(line_1='123 Some St',
+ line_2='Apt 1234',
+ city='Seattle',
+ state='WA',
+ zip_code='98000',),
+ phone='123-456-8762',
+ )
+
+ home_dct = home.to_dict()
+
+ assert home_dct['name'] == "The Barkers"
+ assert home_dct['address']['city'] == 'Seattle'
+
+
+def test_household_to_dict_to_object(chris, fred):
+
+ home = model.Household(name="The Barkers",
+ people=(chris, fred),
+ address=model.Address(line_1='123 Some St',
+ line_2='Apt 1234',
+ city='Seattle',
+ state='WA',
+ zip_code='98000',),
+ phone='123-456-8762',
+ )
+
+ home2 = model.Household.from_dict(home.to_dict())
+
+ assert home2.name == home.name
+ assert home2.phone == home.phone
+ assert home2.address.line_1 == home.address.line_1
+ assert home2.address.line_2 == home.address.line_2
+ assert home2.people == home.people
diff --git a/_downloads/test_calculator_pytest.py b/_downloads/test_calculator_pytest.py
new file mode 100644
index 00000000..0482978a
--- /dev/null
+++ b/_downloads/test_calculator_pytest.py
@@ -0,0 +1,39 @@
+#!/use/bin/env python
+
+"""
+tests for the calculator module
+
+designed to be run with pytest
+"""
+
+import pytest
+
+import calculator_functions as calc
+
+
+# a very simple test
+def test_add():
+ assert calc.add(2, 3) == 5
+
+
+# testing with a variety of parameters:
+def test_multiply_ugly():
+ """
+ the ugly, not very robust way....
+ """
+ assert calc.multiply(2, 2) == 4
+ assert calc.multiply(2, -1) == -2
+ assert calc.multiply(-2, -3) == 6
+ assert calc.multiply(3, 0) == 0
+ assert calc.multiply(0, 3) == 0
+
+
+param_names = "arg1, arg2, result"
+params = [(2, 2, 4),
+ (2, -1, -2),
+ (-2, -2, 4),
+ (3, 0, 0),
+ ]
+@pytest.mark.parametrize(param_names, params)
+def test_multiply(arg1, arg2, result):
+ assert calc.multiply(arg1, arg2) == result
diff --git a/_downloads/test_generator.py b/_downloads/test_generator.py
new file mode 100644
index 00000000..06409e6f
--- /dev/null
+++ b/_downloads/test_generator.py
@@ -0,0 +1,61 @@
+"""
+test_generator.py
+
+tests the solution to the generator lab
+
+can be run with pytest
+"""
+
+import generator_solution as gen
+
+
+def test_intsum():
+
+ g = gen.intsum()
+
+ assert next(g) == 0
+ assert next(g) == 1
+ assert next(g) == 3
+ assert next(g) == 6
+ assert next(g) == 10
+ assert next(g) == 15
+
+
+def test_intsum2():
+
+ g = gen.intsum2()
+
+ assert next(g) == 0
+ assert next(g) == 1
+ assert next(g) == 3
+ assert next(g) == 6
+ assert next(g) == 10
+ assert next(g) == 15
+
+
+def test_doubler():
+
+ g = gen.doubler()
+
+ assert next(g) == 1
+ assert next(g) == 2
+ assert next(g) == 4
+ assert next(g) == 8
+ assert next(g) == 16
+ assert next(g) == 32
+
+ for i in range(10):
+ j = next(g)
+
+ assert j == 2**15
+
+
+def test_fib():
+ g = gen.fib()
+ assert [next(g) for i in range(9)] == [1, 1, 2, 3, 5, 8, 13, 21, 34]
+
+
+def test_prime():
+ g = gen.prime()
+ for val in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]:
+ assert next(g) == val
diff --git a/_downloads/test_html_output2.html b/_downloads/test_html_output2.html
new file mode 100644
index 00000000..4908a122
--- /dev/null
+++ b/_downloads/test_html_output2.html
@@ -0,0 +1,10 @@
+
+
+
+Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
+
+And here is another piece of text -- you should be able to add any number
+
+
+
\ No newline at end of file
diff --git a/_downloads/test_html_output3.html b/_downloads/test_html_output3.html
new file mode 100644
index 00000000..3e19aa10
--- /dev/null
+++ b/_downloads/test_html_output3.html
@@ -0,0 +1,13 @@
+
+
+PythonClass = Revision 1087:
+
+
+
+Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
+
+And here is another piece of text -- you should be able to add any number
+
+
+
diff --git a/_downloads/test_html_output4.html b/_downloads/test_html_output4.html
new file mode 100644
index 00000000..5846b22f
--- /dev/null
+++ b/_downloads/test_html_output4.html
@@ -0,0 +1,10 @@
+
+
+PythonClass = Revision 1087:
+
+
+
+Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
+
+
\ No newline at end of file
diff --git a/_downloads/test_html_output5.html b/_downloads/test_html_output5.html
new file mode 100644
index 00000000..e7efbfc9
--- /dev/null
+++ b/_downloads/test_html_output5.html
@@ -0,0 +1,11 @@
+
+
+PythonClass = Revision 1087:
+
+
+
+Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
+
+
+
\ No newline at end of file
diff --git a/_downloads/test_html_output6.html b/_downloads/test_html_output6.html
new file mode 100644
index 00000000..407a87f7
--- /dev/null
+++ b/_downloads/test_html_output6.html
@@ -0,0 +1,14 @@
+
+
+PythonClass = Revision 1087:
+
+
+
+Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
+
+And this is a
+link
+to google
+
+
diff --git a/_downloads/test_html_output7.html b/_downloads/test_html_output7.html
new file mode 100644
index 00000000..19cfc412
--- /dev/null
+++ b/_downloads/test_html_output7.html
@@ -0,0 +1,25 @@
+
+
+PythonClass = Revision 1087:
+
+
+PythonClass - Class 6 example
+
+Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
+
+
+-
+The first item in a list
+
+-
+This is the second item
+
+-
+And this is a
+link
+to google
+
+
+
+
\ No newline at end of file
diff --git a/_downloads/test_html_output8.html b/_downloads/test_html_output8.html
new file mode 100644
index 00000000..3ae18415
--- /dev/null
+++ b/_downloads/test_html_output8.html
@@ -0,0 +1,27 @@
+
+
+
+
+PythonClass = Revision 1087:
+
+
+PythonClass - Class 6 example
+
+Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
+
+
+-
+The first item in a list
+
+-
+This is the second item
+
+-
+And this is a
+link
+to google
+
+
+
+
\ No newline at end of file
diff --git a/source/exercises/sample_html.html b/_downloads/test_html_output9.html
similarity index 89%
rename from source/exercises/sample_html.html
rename to _downloads/test_html_output9.html
index f2687e95..05dfff23 100644
--- a/source/exercises/sample_html.html
+++ b/_downloads/test_html_output9.html
@@ -10,7 +10,7 @@ PythonClass - Class 6 example
Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
-
+
-
The first item in a list
@@ -18,7 +18,7 @@ PythonClass - Class 6 example
This is the second item
-
- And this is a
+ And this is a
link
to google
diff --git a/_downloads/test_html_render.py b/_downloads/test_html_render.py
new file mode 100644
index 00000000..5833d504
--- /dev/null
+++ b/_downloads/test_html_render.py
@@ -0,0 +1,264 @@
+"""
+test code for html_render.py
+
+This is just a start -- you will need more tests!
+"""
+
+import io
+import pytest
+
+# import * is often bad form, but makes it easier to test everything in a module.
+from html_render import *
+
+
+# utility function for testing render methods
+# needs to be used in multiple tests, so we write it once here.
+def render_result(element, ind=""):
+ """
+ calls the element's render method, and returns what got rendered as a
+ string
+ """
+ # the StringIO object is a "file-like" object -- something that
+ # provides the methods of a file, but keeps everything in memory
+ # so it can be used to test code that writes to a file, without
+ # having to actually write to disk.
+ outfile = io.StringIO()
+ # this so the tests will work before we tackle indentation
+ if ind:
+ element.render(outfile, ind)
+ else:
+ element.render(outfile)
+ return outfile.getvalue()
+
+########
+# Step 1
+########
+
+def test_init():
+ """
+ This only tests that it can be initialized with and without
+ some content -- but it's a start
+ """
+ e = Element()
+
+ e = Element("this is some text")
+
+
+def test_append():
+ """
+ This tests that you can append text
+
+ It doesn't test if it works --
+ that will be covered by the render test later
+ """
+ e = Element("this is some text")
+ e.append("some more text")
+
+
+def test_render_element():
+ """
+ Tests whether the Element can render two pieces of text
+ So it is also testing that the append method works correctly.
+
+ It is not testing whether indentation or line feeds are correct.
+ """
+ e = Element("this is some text")
+ e.append("and this is some more text")
+
+ # This uses the render_results utility above
+ file_contents = render_result(e).strip()
+
+ # making sure the content got in there.
+ assert("this is some text") in file_contents
+ assert("and this is some more text") in file_contents
+
+ # make sure it's in the right order
+ assert file_contents.index("this is") < file_contents.index("and this")
+
+ # making sure the opening and closing tags are right.
+ assert file_contents.startswith("")
+ assert file_contents.endswith("")
+
+# # Uncomment this one after you get the one above to pass
+# # Does it pass right away?
+# def test_render_element2():
+# """
+# Tests whether the Element can render two pieces of text
+# So it is also testing that the append method works correctly.
+
+# It is not testing whether indentation or line feeds are correct.
+# """
+# e = Element()
+# e.append("this is some text")
+# e.append("and this is some more text")
+
+# # This uses the render_results utility above
+# file_contents = render_result(e).strip()
+
+# # making sure the content got in there.
+# assert("this is some text") in file_contents
+# assert("and this is some more text") in file_contents
+
+# # make sure it's in the right order
+# assert file_contents.index("this is") < file_contents.index("and this")
+
+# # making sure the opening and closing tags are right.
+# assert file_contents.startswith("")
+# assert file_contents.endswith("")
+
+
+
+# # ########
+# # # Step 2
+# # ########
+
+# # tests for the new tags
+# def test_html():
+# e = Html("this is some text")
+# e.append("and this is some more text")
+
+# file_contents = render_result(e).strip()
+
+# assert("this is some text") in file_contents
+# assert("and this is some more text") in file_contents
+# print(file_contents)
+# assert file_contents.endswith("