From 37d3e2b73ca2877c7e2e3efd83ae34483e5ef094 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 14 Apr 2021 23:20:15 -0300 Subject: [PATCH 001/127] Add persistent lib to Chapter 25 --- 25-class-metaprog/persistent/.gitignore | 1 + 25-class-metaprog/persistent/dblib.py | 151 +++++++++++++++++++++ 25-class-metaprog/persistent/dblib_test.py | 131 ++++++++++++++++++ 25-class-metaprog/persistent/persistlib.py | 119 ++++++++++++++++ 4 files changed, 402 insertions(+) create mode 100644 25-class-metaprog/persistent/.gitignore create mode 100644 25-class-metaprog/persistent/dblib.py create mode 100644 25-class-metaprog/persistent/dblib_test.py create mode 100644 25-class-metaprog/persistent/persistlib.py diff --git a/25-class-metaprog/persistent/.gitignore b/25-class-metaprog/persistent/.gitignore new file mode 100644 index 0000000..98e6ef6 --- /dev/null +++ b/25-class-metaprog/persistent/.gitignore @@ -0,0 +1 @@ +*.db diff --git a/25-class-metaprog/persistent/dblib.py b/25-class-metaprog/persistent/dblib.py new file mode 100644 index 0000000..68dfeab --- /dev/null +++ b/25-class-metaprog/persistent/dblib.py @@ -0,0 +1,151 @@ +# SQLite3 does not support parameterized table and field names, +# for CREATE TABLE and PRAGMA so we must use Python string formatting. +# Applying `check_identifier` to parameters prevents SQL injection. + +import sqlite3 +from typing import NamedTuple + +DEFAULT_DB_PATH = ':memory:' +CONNECTION = None + +SQL_TYPES = { + int: 'INTEGER', + str: 'TEXT', + float: 'REAL', + bytes: 'BLOB', +} + + +class NoConnection(Exception): + """Call connect() to open connection.""" + + +class SchemaMismatch(ValueError): + """The table's schema doesn't match the class.""" + + def __init__(self, table_name): + self.table_name = table_name + + +class NoSuchRecord(LookupError): + """The given primary key does not exist.""" + + def __init__(self, pk): + self.pk = pk + + +class UnexpectedMultipleResults(Exception): + """Query returned more than 1 row.""" + + +class ColumnSchema(NamedTuple): + name: str + sql_type: str + + +def check_identifier(name): + if not name.isidentifier(): + raise ValueError(f'{name!r} is not an identifier') + + +def connect(db_path=DEFAULT_DB_PATH): + global CONNECTION + CONNECTION = sqlite3.connect(db_path) + return CONNECTION + + +def get_connection(): + if CONNECTION is None: + raise NoConnection() + return CONNECTION + + +def gen_columns_sql(fields): + for name, py_type in fields.items(): + check_identifier(name) + try: + sql_type = SQL_TYPES[py_type] + except KeyError as e: + raise ValueError(f'type {py_type!r} is not supported') from e + yield ColumnSchema(name, sql_type) + + +def make_schema_sql(table_name, fields): + check_identifier(table_name) + pk = 'pk INTEGER PRIMARY KEY,' + spcs = ' ' * 4 + columns = ',\n '.join( + f'{field_name} {sql_type}' + for field_name, sql_type in gen_columns_sql(fields) + ) + return f'CREATE TABLE {table_name} (\n{spcs}{pk}\n{spcs}{columns}\n)' + + +def create_table(table_name, fields): + con = get_connection() + con.execute(make_schema_sql(table_name, fields)) + + +def read_columns_sql(table_name): + con = get_connection() + check_identifier(table_name) + rows = con.execute(f'PRAGMA table_info({table_name!r})') + # row fields: cid name type notnull dflt_value pk + return [ColumnSchema(r[1], r[2]) for r in rows] + + +def valid_table(table_name, fields): + table_columns = read_columns_sql(table_name) + return set(gen_columns_sql(fields)) <= set(table_columns) + + +def ensure_table(table_name, fields): + table_columns = read_columns_sql(table_name) + if len(table_columns) == 0: + create_table(table_name, fields) + if not valid_table(table_name, fields): + raise SchemaMismatch(table_name) + + +def insert_record(table_name, fields): + con = get_connection() + check_identifier(table_name) + placeholders = ', '.join(['?'] * len(fields)) + sql = f'INSERT INTO {table_name} VALUES (NULL, {placeholders})' + cursor = con.execute(sql, tuple(fields.values())) + pk = cursor.lastrowid + con.commit() + cursor.close() + return pk + + +def fetch_record(table_name, pk): + con = get_connection() + check_identifier(table_name) + sql = f'SELECT * FROM {table_name} WHERE pk = ? LIMIT 2' + result = list(con.execute(sql, (pk,))) + if len(result) == 0: + raise NoSuchRecord(pk) + elif len(result) == 1: + return result[0] + else: + raise UnexpectedMultipleResults() + + +def update_record(table_name, pk, fields): + con = get_connection() + check_identifier(table_name) + names = ', '.join(fields.keys()) + placeholders = ', '.join(['?'] * len(fields)) + values = tuple(fields.values()) + (pk,) + sql = f'UPDATE {table_name} SET ({names}) = ({placeholders}) WHERE pk = ?' + con.execute(sql, values) + con.commit() + return sql, values + + +def delete_record(table_name, pk): + con = get_connection() + check_identifier(table_name) + sql = f'DELETE FROM {table_name} WHERE pk = ?' + return con.execute(sql, (pk,)) diff --git a/25-class-metaprog/persistent/dblib_test.py b/25-class-metaprog/persistent/dblib_test.py new file mode 100644 index 0000000..9a0a93c --- /dev/null +++ b/25-class-metaprog/persistent/dblib_test.py @@ -0,0 +1,131 @@ +from textwrap import dedent + +import pytest + +from dblib import gen_columns_sql, make_schema_sql, connect, read_columns_sql +from dblib import ColumnSchema, insert_record, fetch_record, update_record +from dblib import NoSuchRecord, delete_record, valid_table + + +@pytest.fixture +def create_movies_sql(): + sql = ''' + CREATE TABLE movies ( + pk INTEGER PRIMARY KEY, + title TEXT, + revenue REAL + ) + ''' + return dedent(sql).strip() + + +@pytest.mark.parametrize( + 'fields, expected', + [ + ( + dict(title=str, awards=int), + [('title', 'TEXT'), ('awards', 'INTEGER')], + ), + ( + dict(picture=bytes, score=float), + [('picture', 'BLOB'), ('score', 'REAL')], + ), + ], +) +def test_gen_columns_sql(fields, expected): + result = list(gen_columns_sql(fields)) + assert result == expected + + +def test_make_schema_sql(create_movies_sql): + fields = dict(title=str, revenue=float) + result = make_schema_sql('movies', fields) + assert result == create_movies_sql + + +def test_read_columns_sql(create_movies_sql): + expected = [ + ColumnSchema(name='pk', sql_type='INTEGER'), + ColumnSchema(name='title', sql_type='TEXT'), + ColumnSchema(name='revenue', sql_type='REAL'), + ] + with connect() as con: + con.execute(create_movies_sql) + result = read_columns_sql('movies') + assert result == expected + + +def test_read_columns_sql_no_such_table(create_movies_sql): + with connect() as con: + con.execute(create_movies_sql) + result = read_columns_sql('no_such_table') + assert result == [] + + +def test_insert_record(create_movies_sql): + fields = dict(title='Frozen', revenue=1_290_000_000) + with connect() as con: + con.execute(create_movies_sql) + for _ in range(3): + result = insert_record('movies', fields) + assert result == 3 + + +def test_fetch_record(create_movies_sql): + fields = dict(title='Frozen', revenue=1_290_000_000) + with connect() as con: + con.execute(create_movies_sql) + pk = insert_record('movies', fields) + row = fetch_record('movies', pk) + assert row == (1, 'Frozen', 1_290_000_000.0) + + +def test_fetch_record_no_such_pk(create_movies_sql): + with connect() as con: + con.execute(create_movies_sql) + with pytest.raises(NoSuchRecord) as e: + fetch_record('movies', 42) + assert e.value.pk == 42 + + +def test_update_record(create_movies_sql): + fields = dict(title='Frozen', revenue=1_290_000_000) + with connect() as con: + con.execute(create_movies_sql) + pk = insert_record('movies', fields) + fields['revenue'] = 1_299_999_999 + sql, values = update_record('movies', pk, fields) + row = fetch_record('movies', pk) + assert sql == 'UPDATE movies SET (title, revenue) = (?, ?) WHERE pk = ?' + assert values == ('Frozen', 1_299_999_999, 1) + assert row == (1, 'Frozen', 1_299_999_999.0) + + +def test_delete_record(create_movies_sql): + fields = dict(title='Frozen', revenue=1_290_000_000) + with connect() as con: + con.execute(create_movies_sql) + pk = insert_record('movies', fields) + delete_record('movies', pk) + with pytest.raises(NoSuchRecord) as e: + fetch_record('movies', pk) + assert e.value.pk == pk + + +def test_persistent_valid_table(create_movies_sql): + fields = dict(title=str, revenue=float) + + with connect() as con: + con.execute(create_movies_sql) + con.commit() + assert valid_table('movies', fields) + + +def test_persistent_valid_table_false(create_movies_sql): + # year field not in movies_sql + fields = dict(title=str, revenue=float, year=int) + + with connect() as con: + con.execute(create_movies_sql) + con.commit() + assert not valid_table('movies', fields) diff --git a/25-class-metaprog/persistent/persistlib.py b/25-class-metaprog/persistent/persistlib.py new file mode 100644 index 0000000..633f48c --- /dev/null +++ b/25-class-metaprog/persistent/persistlib.py @@ -0,0 +1,119 @@ +""" +A ``Persistent`` class definition:: + + >>> class Movie(Persistent): + ... title: str + ... year: int + ... boxmega: float + +Implemented behavior:: + + >>> Movie._connect() # doctest: +ELLIPSIS + + >>> movie = Movie('The Godfather', 1972, 137) + >>> movie.title + 'The Godfather' + >>> movie.boxmega + 137.0 + +Instances always have a ``.pk`` attribute, but it is ``None`` until the +object is saved:: + + >>> movie.pk is None + True + >>> movie._persist() + >>> movie.pk + 1 + +Delete the in-memory ``movie``, and fetch the record from the database, +using ``Movie[pk]``—item access on the class itself:: + + >>> del movie + >>> film = Movie[1] + >>> film + Movie('The Godfather', 1972, 137.0, pk=1) + +By default, the table name is the class name lowercased, with an appended +"s" for plural:: + + >>> Movie._TABLE_NAME + 'movies' + +If needed, a custom table name can be given as a keyword argument in the +class declaration:: + + >>> class Aircraft(Persistent, table='aircraft'): + ... registration: str + ... model: str + ... + >>> Aircraft._TABLE_NAME + 'aircraft' + +""" + +from typing import get_type_hints + +import dblib as db + + +class Field: + def __init__(self, name, py_type): + self.name = name + self.type = py_type + + def __set__(self, instance, value): + try: + value = self.type(value) + except TypeError as e: + msg = f'{value!r} is not compatible with {self.name}:{self.type}.' + raise TypeError(msg) from e + instance.__dict__[self.name] = value + + +class Persistent: + def __init_subclass__( + cls, *, db_path=db.DEFAULT_DB_PATH, table='', **kwargs + ): + super().__init_subclass__(**kwargs) + cls._TABLE_NAME = table if table else cls.__name__.lower() + 's' + cls._TABLE_READY = False + for name, py_type in get_type_hints(cls).items(): + setattr(cls, name, Field(name, py_type)) + + @staticmethod + def _connect(db_path=db.DEFAULT_DB_PATH): + return db.connect(db_path) + + @classmethod + def _ensure_table(cls): + if not cls._TABLE_READY: + db.ensure_table(cls._TABLE_NAME, get_type_hints(cls)) + cls._TABLE_READY = True + return cls._TABLE_NAME + + def _fields(self): + return { + name: getattr(self, name) + for name, attr in self.__class__.__dict__.items() + if isinstance(attr, Field) + } + + def __init__(self, *args, pk=None): + for name, arg in zip(self._fields(), args): + setattr(self, name, arg) + self.pk = pk + + def __class_getitem__(cls, pk): + return cls(*db.fetch_record(cls._TABLE_NAME, pk)[1:], pk=pk) + + def __repr__(self): + args = ', '.join(repr(value) for value in self._fields().values()) + pk = '' if self.pk is None else f', pk={self.pk}' + return f'{self.__class__.__name__}({args}{pk})' + + def _persist(self): + table = self.__class__._ensure_table() + if self.pk is None: + self.pk = db.insert_record(table, self._fields()) + else: + db.update_record(table, self.pk, self._fields()) From f8a1268fb10c8d1f4fdc24caacce572035904561 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 15 Apr 2021 12:50:50 -0300 Subject: [PATCH 002/127] dblib.update_record: Get connection only if identifiers are ok Co-authored-by: Leonardo Rochael Almeida --- 25-class-metaprog/persistent/dblib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/25-class-metaprog/persistent/dblib.py b/25-class-metaprog/persistent/dblib.py index 68dfeab..c858e3c 100644 --- a/25-class-metaprog/persistent/dblib.py +++ b/25-class-metaprog/persistent/dblib.py @@ -133,8 +133,8 @@ def fetch_record(table_name, pk): def update_record(table_name, pk, fields): - con = get_connection() check_identifier(table_name) + con = get_connection() names = ', '.join(fields.keys()) placeholders = ', '.join(['?'] * len(fields)) values = tuple(fields.values()) + (pk,) From ee418d7d972baf418c1f01f17736e80b214d3338 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 15 Apr 2021 13:34:17 -0300 Subject: [PATCH 003/127] Fix unneeded posessive 's in docstring Co-authored-by: Leonardo Rochael Almeida --- 25-class-metaprog/persistent/dblib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/25-class-metaprog/persistent/dblib.py b/25-class-metaprog/persistent/dblib.py index c858e3c..e81edc4 100644 --- a/25-class-metaprog/persistent/dblib.py +++ b/25-class-metaprog/persistent/dblib.py @@ -21,7 +21,7 @@ class NoConnection(Exception): class SchemaMismatch(ValueError): - """The table's schema doesn't match the class.""" + """The table schema doesn't match the class.""" def __init__(self, table_name): self.table_name = table_name From 4ff0a59608a55af62a7539fb8a886c7eb766a4d9 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 15 Apr 2021 17:15:53 -0300 Subject: [PATCH 004/127] refactoring after reviewers feedback + type hints --- 25-class-metaprog/persistent/dblib.py | 76 +++++++------- 25-class-metaprog/persistent/dblib_test.py | 4 +- 25-class-metaprog/persistent/persistlib.py | 99 +++++++++++-------- .../persistent/persistlib_test.py | 37 +++++++ 4 files changed, 142 insertions(+), 74 deletions(-) create mode 100644 25-class-metaprog/persistent/persistlib_test.py diff --git a/25-class-metaprog/persistent/dblib.py b/25-class-metaprog/persistent/dblib.py index e81edc4..dd1fae4 100644 --- a/25-class-metaprog/persistent/dblib.py +++ b/25-class-metaprog/persistent/dblib.py @@ -3,17 +3,10 @@ # Applying `check_identifier` to parameters prevents SQL injection. import sqlite3 -from typing import NamedTuple +from typing import NamedTuple, Optional, Iterator, Any DEFAULT_DB_PATH = ':memory:' -CONNECTION = None - -SQL_TYPES = { - int: 'INTEGER', - str: 'TEXT', - float: 'REAL', - bytes: 'BLOB', -} +CONNECTION: Optional[sqlite3.Connection] = None class NoConnection(Exception): @@ -38,29 +31,45 @@ class UnexpectedMultipleResults(Exception): """Query returned more than 1 row.""" +SQLType = str + +TypeMap = dict[type, SQLType] + +SQL_TYPES: TypeMap = { + int: 'INTEGER', + str: 'TEXT', + float: 'REAL', + bytes: 'BLOB', +} + + class ColumnSchema(NamedTuple): name: str - sql_type: str + sql_type: SQLType -def check_identifier(name): +FieldMap = dict[str, type] + + +def check_identifier(name: str) -> None: if not name.isidentifier(): raise ValueError(f'{name!r} is not an identifier') -def connect(db_path=DEFAULT_DB_PATH): +def connect(db_path: str = DEFAULT_DB_PATH) -> sqlite3.Connection: global CONNECTION CONNECTION = sqlite3.connect(db_path) + CONNECTION.row_factory = sqlite3.Row return CONNECTION -def get_connection(): +def get_connection() -> sqlite3.Connection: if CONNECTION is None: raise NoConnection() return CONNECTION -def gen_columns_sql(fields): +def gen_columns_sql(fields: FieldMap) -> Iterator[ColumnSchema]: for name, py_type in fields.items(): check_identifier(name) try: @@ -70,7 +79,7 @@ def gen_columns_sql(fields): yield ColumnSchema(name, sql_type) -def make_schema_sql(table_name, fields): +def make_schema_sql(table_name: str, fields: FieldMap) -> str: check_identifier(table_name) pk = 'pk INTEGER PRIMARY KEY,' spcs = ' ' * 4 @@ -81,25 +90,24 @@ def make_schema_sql(table_name, fields): return f'CREATE TABLE {table_name} (\n{spcs}{pk}\n{spcs}{columns}\n)' -def create_table(table_name, fields): +def create_table(table_name: str, fields: FieldMap) -> None: con = get_connection() con.execute(make_schema_sql(table_name, fields)) -def read_columns_sql(table_name): - con = get_connection() +def read_columns_sql(table_name: str) -> list[ColumnSchema]: check_identifier(table_name) + con = get_connection() rows = con.execute(f'PRAGMA table_info({table_name!r})') - # row fields: cid name type notnull dflt_value pk - return [ColumnSchema(r[1], r[2]) for r in rows] + return [ColumnSchema(r['name'], r['type']) for r in rows] -def valid_table(table_name, fields): +def valid_table(table_name: str, fields: FieldMap) -> bool: table_columns = read_columns_sql(table_name) return set(gen_columns_sql(fields)) <= set(table_columns) -def ensure_table(table_name, fields): +def ensure_table(table_name: str, fields: FieldMap) -> None: table_columns = read_columns_sql(table_name) if len(table_columns) == 0: create_table(table_name, fields) @@ -107,21 +115,21 @@ def ensure_table(table_name, fields): raise SchemaMismatch(table_name) -def insert_record(table_name, fields): - con = get_connection() +def insert_record(table_name: str, data: dict[str, Any]) -> int: check_identifier(table_name) - placeholders = ', '.join(['?'] * len(fields)) + con = get_connection() + placeholders = ', '.join(['?'] * len(data)) sql = f'INSERT INTO {table_name} VALUES (NULL, {placeholders})' - cursor = con.execute(sql, tuple(fields.values())) + cursor = con.execute(sql, tuple(data.values())) pk = cursor.lastrowid con.commit() cursor.close() return pk -def fetch_record(table_name, pk): - con = get_connection() +def fetch_record(table_name: str, pk: int) -> sqlite3.Row: check_identifier(table_name) + con = get_connection() sql = f'SELECT * FROM {table_name} WHERE pk = ? LIMIT 2' result = list(con.execute(sql, (pk,))) if len(result) == 0: @@ -132,19 +140,21 @@ def fetch_record(table_name, pk): raise UnexpectedMultipleResults() -def update_record(table_name, pk, fields): +def update_record( + table_name: str, pk: int, data: dict[str, Any] +) -> tuple[str, tuple[Any, ...]]: check_identifier(table_name) con = get_connection() - names = ', '.join(fields.keys()) - placeholders = ', '.join(['?'] * len(fields)) - values = tuple(fields.values()) + (pk,) + names = ', '.join(data.keys()) + placeholders = ', '.join(['?'] * len(data)) + values = tuple(data.values()) + (pk,) sql = f'UPDATE {table_name} SET ({names}) = ({placeholders}) WHERE pk = ?' con.execute(sql, values) con.commit() return sql, values -def delete_record(table_name, pk): +def delete_record(table_name: str, pk: int) -> sqlite3.Cursor: con = get_connection() check_identifier(table_name) sql = f'DELETE FROM {table_name} WHERE pk = ?' diff --git a/25-class-metaprog/persistent/dblib_test.py b/25-class-metaprog/persistent/dblib_test.py index 9a0a93c..dcaf0bb 100644 --- a/25-class-metaprog/persistent/dblib_test.py +++ b/25-class-metaprog/persistent/dblib_test.py @@ -77,7 +77,7 @@ def test_fetch_record(create_movies_sql): con.execute(create_movies_sql) pk = insert_record('movies', fields) row = fetch_record('movies', pk) - assert row == (1, 'Frozen', 1_290_000_000.0) + assert tuple(row) == (1, 'Frozen', 1_290_000_000.0) def test_fetch_record_no_such_pk(create_movies_sql): @@ -98,7 +98,7 @@ def test_update_record(create_movies_sql): row = fetch_record('movies', pk) assert sql == 'UPDATE movies SET (title, revenue) = (?, ?) WHERE pk = ?' assert values == ('Frozen', 1_299_999_999, 1) - assert row == (1, 'Frozen', 1_299_999_999.0) + assert tuple(row) == (1, 'Frozen', 1_299_999_999.0) def test_delete_record(create_movies_sql): diff --git a/25-class-metaprog/persistent/persistlib.py b/25-class-metaprog/persistent/persistlib.py index 633f48c..15c9d38 100644 --- a/25-class-metaprog/persistent/persistlib.py +++ b/25-class-metaprog/persistent/persistlib.py @@ -4,25 +4,26 @@ >>> class Movie(Persistent): ... title: str ... year: int - ... boxmega: float + ... megabucks: float Implemented behavior:: >>> Movie._connect() # doctest: +ELLIPSIS - >>> movie = Movie('The Godfather', 1972, 137) + >>> movie = Movie(title='The Godfather', year=1972, megabucks=137) >>> movie.title 'The Godfather' - >>> movie.boxmega + >>> movie.megabucks 137.0 -Instances always have a ``.pk`` attribute, but it is ``None`` until the +Instances always have a ``._pk`` attribute, but it is ``None`` until the object is saved:: - >>> movie.pk is None + >>> movie._pk is None True - >>> movie._persist() - >>> movie.pk + >>> movie._save() + 1 + >>> movie._pk 1 Delete the in-memory ``movie``, and fetch the record from the database, @@ -31,7 +32,7 @@ >>> del movie >>> film = Movie[1] >>> film - Movie('The Godfather', 1972, 137.0, pk=1) + Movie(title='The Godfather', year=1972, megabucks=137.0, _pk=1) By default, the table name is the class name lowercased, with an appended "s" for plural:: @@ -51,69 +52,89 @@ class declaration:: """ -from typing import get_type_hints +from typing import Any, ClassVar, get_type_hints import dblib as db class Field: - def __init__(self, name, py_type): + def __init__(self, name: str, py_type: type) -> None: self.name = name self.type = py_type - def __set__(self, instance, value): + def __set__(self, instance: 'Persistent', value: Any) -> None: try: value = self.type(value) - except TypeError as e: - msg = f'{value!r} is not compatible with {self.name}:{self.type}.' + except (TypeError, ValueError) as e: + type_name = self.type.__name__ + msg = f'{value!r} is not compatible with {self.name}:{type_name}.' raise TypeError(msg) from e instance.__dict__[self.name] = value class Persistent: - def __init_subclass__( - cls, *, db_path=db.DEFAULT_DB_PATH, table='', **kwargs - ): - super().__init_subclass__(**kwargs) + _TABLE_NAME: ClassVar[str] + _TABLE_READY: ClassVar[bool] = False + + @classmethod + def _fields(cls) -> dict[str, type]: + return { + name: py_type + for name, py_type in get_type_hints(cls).items() + if not name.startswith('_') + } + + def __init_subclass__(cls, *, table: str = '', **kwargs: dict): + super().__init_subclass__(**kwargs) # type:ignore cls._TABLE_NAME = table if table else cls.__name__.lower() + 's' - cls._TABLE_READY = False - for name, py_type in get_type_hints(cls).items(): + for name, py_type in cls._fields().items(): setattr(cls, name, Field(name, py_type)) @staticmethod - def _connect(db_path=db.DEFAULT_DB_PATH): + def _connect(db_path: str = db.DEFAULT_DB_PATH): return db.connect(db_path) @classmethod - def _ensure_table(cls): + def _ensure_table(cls) -> str: if not cls._TABLE_READY: - db.ensure_table(cls._TABLE_NAME, get_type_hints(cls)) + db.ensure_table(cls._TABLE_NAME, cls._fields()) cls._TABLE_READY = True return cls._TABLE_NAME - def _fields(self): + def __class_getitem__(cls, pk: int) -> 'Persistent': + field_names = ['_pk'] + list(cls._fields()) + values = db.fetch_record(cls._TABLE_NAME, pk) + return cls(**dict(zip(field_names, values))) + + def _asdict(self) -> dict[str, Any]: return { name: getattr(self, name) for name, attr in self.__class__.__dict__.items() if isinstance(attr, Field) } - def __init__(self, *args, pk=None): - for name, arg in zip(self._fields(), args): + def __init__(self, *, _pk=None, **kwargs): + field_names = self._asdict().keys() + for name, arg in kwargs.items(): + if name not in field_names: + msg = f'{self.__class__.__name__!r} has no attribute {name!r}' + raise AttributeError(msg) setattr(self, name, arg) - self.pk = pk - - def __class_getitem__(cls, pk): - return cls(*db.fetch_record(cls._TABLE_NAME, pk)[1:], pk=pk) - - def __repr__(self): - args = ', '.join(repr(value) for value in self._fields().values()) - pk = '' if self.pk is None else f', pk={self.pk}' - return f'{self.__class__.__name__}({args}{pk})' - - def _persist(self): + self._pk = _pk + + def __repr__(self) -> str: + kwargs = ', '.join( + f'{key}={value!r}' for key, value in self._asdict().items() + ) + cls_name = self.__class__.__name__ + if self._pk is None: + return f'{cls_name}({kwargs})' + return f'{cls_name}({kwargs}, _pk={self._pk})' + + def _save(self) -> int: table = self.__class__._ensure_table() - if self.pk is None: - self.pk = db.insert_record(table, self._fields()) + if self._pk is None: + self._pk = db.insert_record(table, self._asdict()) else: - db.update_record(table, self.pk, self._fields()) + db.update_record(table, self._pk, self._asdict()) + return self._pk diff --git a/25-class-metaprog/persistent/persistlib_test.py b/25-class-metaprog/persistent/persistlib_test.py new file mode 100644 index 0000000..1604ccb --- /dev/null +++ b/25-class-metaprog/persistent/persistlib_test.py @@ -0,0 +1,37 @@ +import pytest + + +from persistlib import Persistent + + +def test_field_descriptor_validation_type_error(): + class Cat(Persistent): + name: str + weight: float + + with pytest.raises(TypeError) as e: + felix = Cat(name='Felix', weight=None) + + assert str(e.value) == 'None is not compatible with weight:float.' + + +def test_field_descriptor_validation_value_error(): + class Cat(Persistent): + name: str + weight: float + + with pytest.raises(TypeError) as e: + felix = Cat(name='Felix', weight='half stone') + + assert str(e.value) == "'half stone' is not compatible with weight:float." + + +def test_constructor_attribute_error(): + class Cat(Persistent): + name: str + weight: float + + with pytest.raises(AttributeError) as e: + felix = Cat(name='Felix', weight=3.2, age=7) + + assert str(e.value) == "'Cat' has no attribute 'age'" From 177d914c9f99e8a4fc6b56a08cf900ec26d27c00 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 17 Apr 2021 21:02:51 -0300 Subject: [PATCH 005/127] ch25: new example with __init_subclas__ --- 25-class-metaprog/checked/checkedlib.py | 143 +++++++++++++++++++ 25-class-metaprog/checked/checkedlib_demo.py | 21 +++ 25-class-metaprog/checked/checkedlib_test.py | 37 +++++ 3 files changed, 201 insertions(+) create mode 100644 25-class-metaprog/checked/checkedlib.py create mode 100644 25-class-metaprog/checked/checkedlib_demo.py create mode 100644 25-class-metaprog/checked/checkedlib_test.py diff --git a/25-class-metaprog/checked/checkedlib.py b/25-class-metaprog/checked/checkedlib.py new file mode 100644 index 0000000..505c915 --- /dev/null +++ b/25-class-metaprog/checked/checkedlib.py @@ -0,0 +1,143 @@ +""" +A ``Checked`` subclass definition requires that keyword arguments are +used to create an instance, and provides a nice ``__repr__``:: + +# tag::MOVIE_DEFINITION[] + + >>> class Movie(Checked): # <1> + ... title: str # <2> + ... year: int + ... megabucks: float + ... + >>> movie = Movie(title='The Godfather', year=1972, megabucks=137) # <3> + >>> movie.title + 'The Godfather' + >>> movie # <4> + Movie(title='The Godfather', year=1972, megabucks=137.0) + +# end::MOVIE_DEFINITION[] + +The type of arguments is runtime checked when an attribute is set, +including during instantiation:: + +# tag::MOVIE_TYPE_VALIDATION[] + + >>> movie.year = 'MCMLXXII' # <1> + Traceback (most recent call last): + ... + TypeError: 'MCMLXXII' is not compatible with year:int + >>> blockbuster = Movie(title='Avatar', year=2009, megabucks='billions') # <2> + Traceback (most recent call last): + ... + TypeError: 'billions' is not compatible with megabucks:float + +# end::MOVIE_TYPE_VALIDATION[] + +Attributes not passed as arguments to the constructor are initialized with +default values:: + +# tag::MOVIE_DEFAULTS[] + + >>> Movie(title='Life of Brian') + Movie(title='Life of Brian', year=0, megabucks=0.0) + +# end::MOVIE_DEFAULTS[] + +Providing extra arguments to the constructor is not allowed:: + + >>> blockbuster = Movie(title='Avatar', year=2009, megabucks=2000, + ... director='James Cameron') + Traceback (most recent call last): + ... + AttributeError: 'Movie' has no attribute 'director' + +Creating new attributes at runtime is restricted as well:: + + >>> movie.director = 'Francis Ford Coppola' + Traceback (most recent call last): + ... + AttributeError: 'Movie' has no attribute 'director' + +The `_as_dict` instance creates a `dict` from the attributes of a `Movie` object:: + + >>> movie._asdict() + {'title': 'The Godfather', 'year': 1972, 'megabucks': 137.0} + +""" + +# tag::CHECKED_FIELD[] +from collections.abc import Callable # <1> +from typing import Any, NoReturn, get_type_hints + +MISSING = object() # <2> + + +class Field: + def __init__(self, name: str, constructor: Callable) -> None: # <3> + self.name = name + self.constructor = constructor + + def __set__(self, instance: 'Checked', value: Any) -> None: # <4> + if value is MISSING: # <5> + value = self.constructor() + else: + try: + value = self.constructor(value) # <6> + except (TypeError, ValueError) as e: + type_name = self.constructor.__name__ + msg = f'{value!r} is not compatible with {self.name}:{type_name}' + raise TypeError(msg) from e + instance.__dict__[self.name] = value # <7> + + +# end::CHECKED_FIELD[] + +# tag::CHECKED_TOP[] +class Checked: + @classmethod + def _fields(cls) -> dict[str, type]: # <1> + return get_type_hints(cls) + + def __init_subclass__(subclass) -> None: # <2> + super().__init_subclass__() # <3> + for name, constructor in subclass._fields().items(): # <4> + setattr(subclass, name, Field(name, constructor)) # <5> + + def __init__(self, **kwargs: Any) -> None: + for name in self._fields(): # <6> + value = kwargs.pop(name, MISSING) # <7> + setattr(self, name, value) # <8> + if kwargs: # <9> + self.__flag_unknown_attrs(*kwargs) + + def __setattr__(self, name: str, value: Any) -> None: # <10> + if name in self._fields(): # <11> + cls = self.__class__ + descriptor = getattr(cls, name) + descriptor.__set__(self, value) # <12> + else: # <13> + self.__flag_unknown_attrs(name) + + # end::CHECKED_TOP[] + + # tag::CHECKED_BOTTOM[] + def __flag_unknown_attrs(self, *names: str) -> NoReturn: # <1> + plural = 's' if len(names) > 1 else '' + extra = ', '.join(f'{name!r}' for name in names) + cls_name = repr(self.__class__.__name__) + raise AttributeError(f'{cls_name} has no attribute{plural} {extra}') + + def _asdict(self) -> dict[str, Any]: # <2> + return { + name: getattr(self, name) + for name, attr in self.__class__.__dict__.items() + if isinstance(attr, Field) + } + + def __repr__(self) -> str: # <3> + kwargs = ', '.join( + f'{key}={value!r}' for key, value in self._asdict().items() + ) + return f'{self.__class__.__name__}({kwargs})' + +# end::CHECKED_BOTTOM[] diff --git a/25-class-metaprog/checked/checkedlib_demo.py b/25-class-metaprog/checked/checkedlib_demo.py new file mode 100644 index 0000000..203f90b --- /dev/null +++ b/25-class-metaprog/checked/checkedlib_demo.py @@ -0,0 +1,21 @@ +from checkedlib import Checked + +class Movie(Checked): + title: str + year: int + megabucks: float + + +if __name__ == '__main__': + movie = Movie(title='The Godfather', year=1972, megabucks=137) + print(movie.title) + print(movie) + try: + # remove the "type: ignore" comment to see Mypy error + movie.year = 'MCMLXXII' # type: ignore + except TypeError as e: + print(e) + try: + blockbuster = Movie(title='Avatar', year=2009, megabucks='billions') + except TypeError as e: + print(e) diff --git a/25-class-metaprog/checked/checkedlib_test.py b/25-class-metaprog/checked/checkedlib_test.py new file mode 100644 index 0000000..6635a66 --- /dev/null +++ b/25-class-metaprog/checked/checkedlib_test.py @@ -0,0 +1,37 @@ +import pytest + + +from checkedlib import Checked + + +def test_field_descriptor_validation_type_error(): + class Cat(Checked): + name: str + weight: float + + with pytest.raises(TypeError) as e: + felix = Cat(name='Felix', weight=None) + + assert str(e.value) == 'None is not compatible with weight:float' + + +def test_field_descriptor_validation_value_error(): + class Cat(Checked): + name: str + weight: float + + with pytest.raises(TypeError) as e: + felix = Cat(name='Felix', weight='half stone') + + assert str(e.value) == "'half stone' is not compatible with weight:float" + + +def test_constructor_attribute_error(): + class Cat(Checked): + name: str + weight: float + + with pytest.raises(AttributeError) as e: + felix = Cat(name='Felix', weight=3.2, age=7) + + assert str(e.value) == "'Cat' has no attribute 'age'" From 674fad84c5942a45b95fa31d6c8a72203de0062d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Apr 2021 20:06:32 +0000 Subject: [PATCH 006/127] build(deps): bump py from 1.8.1 to 1.10.0 in /10-dp-1class-func Bumps [py](https://github.com/pytest-dev/py) from 1.8.1 to 1.10.0. - [Release notes](https://github.com/pytest-dev/py/releases) - [Changelog](https://github.com/pytest-dev/py/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/py/compare/1.8.1...1.10.0) Signed-off-by: dependabot[bot] --- 10-dp-1class-func/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/10-dp-1class-func/requirements.txt b/10-dp-1class-func/requirements.txt index 2b20c05..1826922 100644 --- a/10-dp-1class-func/requirements.txt +++ b/10-dp-1class-func/requirements.txt @@ -6,7 +6,7 @@ attrs==19.3.0 more-itertools==8.2.0 packaging==20.3 pluggy==0.13.1 -py==1.8.1 +py==1.10.0 pyparsing==2.4.6 pytest==5.4.1 six==1.14.0 From 8ec5fd08612c99c9c130e0f20346bb24b6874924 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 22 Apr 2021 19:39:49 -0300 Subject: [PATCH 007/127] ch25: added checkeddeco example --- 25-class-metaprog/checked/checkedlib.py | 20 +-- 25-class-metaprog/checkeddeco/checkeddeco.py | 170 ++++++++++++++++++ .../checkeddeco/checkeddeco_demo.py | 22 +++ .../checkeddeco/checkeddeco_test.py | 40 +++++ 4 files changed, 242 insertions(+), 10 deletions(-) create mode 100644 25-class-metaprog/checkeddeco/checkeddeco.py create mode 100644 25-class-metaprog/checkeddeco/checkeddeco_demo.py create mode 100644 25-class-metaprog/checkeddeco/checkeddeco_test.py diff --git a/25-class-metaprog/checked/checkedlib.py b/25-class-metaprog/checked/checkedlib.py index 505c915..63442e3 100644 --- a/25-class-metaprog/checked/checkedlib.py +++ b/25-class-metaprog/checked/checkedlib.py @@ -104,18 +104,18 @@ def __init_subclass__(subclass) -> None: # <2> setattr(subclass, name, Field(name, constructor)) # <5> def __init__(self, **kwargs: Any) -> None: - for name in self._fields(): # <6> - value = kwargs.pop(name, MISSING) # <7> - setattr(self, name, value) # <8> - if kwargs: # <9> - self.__flag_unknown_attrs(*kwargs) - - def __setattr__(self, name: str, value: Any) -> None: # <10> - if name in self._fields(): # <11> + for name in self._fields(): # <6> + value = kwargs.pop(name, MISSING) # <7> + setattr(self, name, value) # <8> + if kwargs: # <9> + self.__flag_unknown_attrs(*kwargs) # <10> + + def __setattr__(self, name: str, value: Any) -> None: # <11> + if name in self._fields(): # <12> cls = self.__class__ descriptor = getattr(cls, name) - descriptor.__set__(self, value) # <12> - else: # <13> + descriptor.__set__(self, value) # <13> + else: # <14> self.__flag_unknown_attrs(name) # end::CHECKED_TOP[] diff --git a/25-class-metaprog/checkeddeco/checkeddeco.py b/25-class-metaprog/checkeddeco/checkeddeco.py new file mode 100644 index 0000000..3eeba15 --- /dev/null +++ b/25-class-metaprog/checkeddeco/checkeddeco.py @@ -0,0 +1,170 @@ +""" +A ``Checked`` subclass definition requires that keyword arguments are +used to create an instance, and provides a nice ``__repr__``:: + +# tag::MOVIE_DEFINITION[] + + >>> @checked + ... class Movie: + ... title: str + ... year: int + ... megabucks: float + ... + >>> movie = Movie(title='The Godfather', year=1972, megabucks=137) # <3> + >>> movie.title + 'The Godfather' + >>> movie # <4> + Movie(title='The Godfather', year=1972, megabucks=137.0) + +# end::MOVIE_DEFINITION[] + +The type of arguments is runtime checked when an attribute is set, +including during instantiation:: + +# tag::MOVIE_TYPE_VALIDATION[] + + >>> movie.year = 'MCMLXXII' # <1> + Traceback (most recent call last): + ... + TypeError: 'MCMLXXII' is not compatible with year:int + >>> blockbuster = Movie(title='Avatar', year=2009, megabucks='billions') # <2> + Traceback (most recent call last): + ... + TypeError: 'billions' is not compatible with megabucks:float + +# end::MOVIE_TYPE_VALIDATION[] + +Attributes not passed as arguments to the constructor are initialized with +default values:: + +# tag::MOVIE_DEFAULTS[] + + >>> Movie(title='Life of Brian') + Movie(title='Life of Brian', year=0, megabucks=0.0) + +# end::MOVIE_DEFAULTS[] + +Providing extra arguments to the constructor is not allowed:: + + >>> blockbuster = Movie(title='Avatar', year=2009, megabucks=2000, + ... director='James Cameron') + Traceback (most recent call last): + ... + AttributeError: 'Movie' has no attribute 'director' + +Creating new attributes at runtime is restricted as well:: + + >>> movie.director = 'Francis Ford Coppola' + Traceback (most recent call last): + ... + AttributeError: 'Movie' has no attribute 'director' + +The `_as_dict` instance creates a `dict` from the attributes of a `Movie` object:: + + >>> movie._asdict() + {'title': 'The Godfather', 'year': 1972, 'megabucks': 137.0} + +""" + +from collections.abc import Callable # <1> +from typing import Any, NoReturn, get_type_hints + +MISSING = object() # <2> + + +class Field: + def __init__(self, name: str, constructor: Callable) -> None: # <3> + self.name = name + self.constructor = constructor + + def __set__(self, instance: Any, value: Any) -> None: # <4> + if value is MISSING: # <5> + value = self.constructor() + else: + try: + value = self.constructor(value) # <6> + except (TypeError, ValueError) as e: + type_name = self.constructor.__name__ + msg = ( + f'{value!r} is not compatible with {self.name}:{type_name}' + ) + raise TypeError(msg) from e + instance.__dict__[self.name] = value # <7> + + +# tag::CHECKED_DECORATOR_TOP[] +_methods_to_inject: list[Callable] = [] +_classmethods_to_inject: list[Callable] = [] + +def checked(cls: type) -> type: # <2> + for func in _methods_to_inject: + name = func.__name__ + setattr(cls, name, func) # <5> + + for func in _classmethods_to_inject: + name = func.__name__ + setattr(cls, name, classmethod(func)) # <5> + + for name, constructor in _fields(cls).items(): # <4> + setattr(cls, name, Field(name, constructor)) # <5> + + return cls + + +def _method(func: Callable) -> Callable: + _methods_to_inject.append(func) + return func + + +def _classmethod(func: Callable) -> Callable: + _classmethods_to_inject.append(func) + return func + +# tag::CHECKED_METHODS_TOP[] +@_classmethod +def _fields(cls: type) -> dict[str, type]: # <1> + return get_type_hints(cls) + +@_method +def __init__(self: Any, **kwargs: Any) -> None: + for name in self._fields(): # <6> + value = kwargs.pop(name, MISSING) # <7> + setattr(self, name, value) # <8> + if kwargs: # <9> + self.__flag_unknown_attrs(*kwargs) # <10> + +@_method +def __setattr__(self: Any, name: str, value: Any) -> None: # <11> + if name in self._fields(): # <12> + cls = self.__class__ + descriptor = getattr(cls, name) + descriptor.__set__(self, value) # <13> + else: # <14> + self.__flag_unknown_attrs(name) +# end::CHECKED_METHODS_TOP[] + +# tag::CHECKED_METHODS_BOTTOM[] +@_method +def __flag_unknown_attrs(self: Any, *names: str) -> NoReturn: # <1> + plural = 's' if len(names) > 1 else '' + extra = ', '.join(f'{name!r}' for name in names) + cls_name = repr(self.__class__.__name__) + raise AttributeError(f'{cls_name} has no attribute{plural} {extra}') + + +@_method +def _asdict(self: Any) -> dict[str, Any]: # <2> + return { + name: getattr(self, name) + for name, attr in self.__class__.__dict__.items() + if isinstance(attr, Field) + } + + +@_method +def __repr__(self: Any) -> str: # <3> + kwargs = ', '.join( + f'{key}={value!r}' for key, value in self._asdict().items() + ) + return f'{self.__class__.__name__}({kwargs})' +# end::CHECKED_METHODS_BOTTOM[] diff --git a/25-class-metaprog/checkeddeco/checkeddeco_demo.py b/25-class-metaprog/checkeddeco/checkeddeco_demo.py new file mode 100644 index 0000000..f4c45e7 --- /dev/null +++ b/25-class-metaprog/checkeddeco/checkeddeco_demo.py @@ -0,0 +1,22 @@ +from checkeddeco import checked + +@checked +class Movie: + title: str + year: int + megabucks: float + + +if __name__ == '__main__': + movie = Movie(title='The Godfather', year=1972, megabucks=137) + print(movie.title) + print(movie) + try: + # remove the "type: ignore" comment to see Mypy error + movie.year = 'MCMLXXII' # type: ignore + except TypeError as e: + print(e) + try: + blockbuster = Movie(title='Avatar', year=2009, megabucks='billions') + except TypeError as e: + print(e) diff --git a/25-class-metaprog/checkeddeco/checkeddeco_test.py b/25-class-metaprog/checkeddeco/checkeddeco_test.py new file mode 100644 index 0000000..7f4d8dc --- /dev/null +++ b/25-class-metaprog/checkeddeco/checkeddeco_test.py @@ -0,0 +1,40 @@ +import pytest + + +from checkeddeco import checked + + +def test_field_descriptor_validation_type_error(): + @checked + class Cat: + name: str + weight: float + + with pytest.raises(TypeError) as e: + felix = Cat(name='Felix', weight=None) + + assert str(e.value) == 'None is not compatible with weight:float' + + +def test_field_descriptor_validation_value_error(): + @checked + class Cat: + name: str + weight: float + + with pytest.raises(TypeError) as e: + felix = Cat(name='Felix', weight='half stone') + + assert str(e.value) == "'half stone' is not compatible with weight:float" + + +def test_constructor_attribute_error(): + @checked + class Cat: + name: str + weight: float + + with pytest.raises(AttributeError) as e: + felix = Cat(name='Felix', weight=3.2, age=7) + + assert str(e.value) == "'Cat' has no attribute 'age'" From eee989cbd68af7f5fefaf74f441aeb66c8ac5718 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 22 Apr 2021 19:43:07 -0300 Subject: [PATCH 008/127] ch10: removed development requirements --- 10-dp-1class-func/requirements.txt | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 10-dp-1class-func/requirements.txt diff --git a/10-dp-1class-func/requirements.txt b/10-dp-1class-func/requirements.txt deleted file mode 100644 index 2b20c05..0000000 --- a/10-dp-1class-func/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -mypy==0.770 -mypy-extensions==0.4.3 -typed-ast==1.4.1 -typing-extensions==3.7.4.1 -attrs==19.3.0 -more-itertools==8.2.0 -packaging==20.3 -pluggy==0.13.1 -py==1.8.1 -pyparsing==2.4.6 -pytest==5.4.1 -six==1.14.0 -wcwidth==0.1.9 From 063a540868c46d4af375bd9983138ea2badcfe30 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 24 Apr 2021 20:49:53 -0300 Subject: [PATCH 009/127] ch22: new README.md and minor edits --- 22-async/mojifinder/README.md | 41 ++++++++++++++++++++ 22-async/mojifinder/bottle.py | 6 +-- 22-async/mojifinder/requirements.txt | 3 +- 22-async/mojifinder/web_mojifinder.py | 9 ++--- 22-async/mojifinder/web_mojifinder_bottle.py | 4 +- 5 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 22-async/mojifinder/README.md diff --git a/22-async/mojifinder/README.md b/22-async/mojifinder/README.md new file mode 100644 index 0000000..16b8c2e --- /dev/null +++ b/22-async/mojifinder/README.md @@ -0,0 +1,41 @@ +# Mojifinder: Unicode character search examples + +Examples from _Fluent Python, Second Edition_—Chapter 22, _Asynchronous Programming_. + +## How to run `web_mojifinder.py` + +`web_mojifinder.py` is a Web application built with _[FastAPI](https://fastapi.tiangolo.com/)_. +To run it, first install _FastAPI_ and an ASGI server. +The application was tested with _[Uvicorn](https://www.uvicorn.org/)_. + +``` +$ pip install fastapi uvicorn +``` + +Now you can use `uvicorn` to run the app. + +``` +$ uvicorn web_mojifinder:app +``` + +Finally, visit http://127.0.0.1:8000/ with your browser to see the search form. + + +## Directory contents + +These files can be run as scripts directly from the command line: + +- `charindex.py`: libray used by the Mojifinder examples. Also works as CLI search script. +- `tcp_mojifinder.py`: TCP/IP Unicode search server. Depends only on the Python 3.9 standard library. Use a telnet application as client. +- `web_mojifinder_bottle.py`: Unicode Web service. Depends on `bottle.py` and `static/form.html`. Use an HTTP browser as client. + +This program requires an ASGI server to run it: + +- `web_mojifinder.py`: Unicode Web service. Depends on _[FastAPI](https://fastapi.tiangolo.com/)_ and `static/form.html`. + +Support files: + +- `bottle.py`: local copy of the single-file _[Bottle](https://bottlepy.org/)_ Web framework. +- `requirements.txt`: list of dependencies for `web_mojifinder.py`. +- `static/form.html`: HTML form used by the `web_*` examples. +- `README.md`: this file! diff --git a/22-async/mojifinder/bottle.py b/22-async/mojifinder/bottle.py index 96ca3e4..9806efd 100644 --- a/22-async/mojifinder/bottle.py +++ b/22-async/mojifinder/bottle.py @@ -16,7 +16,7 @@ from __future__ import with_statement __author__ = 'Marcel Hellkamp' -__version__ = '0.12.18' +__version__ = '0.12.19' __license__ = 'MIT' # The gevent server adapter needs to patch some modules before they are imported @@ -440,7 +440,7 @@ def match(self, environ): nocheck = set(methods) for method in set(self.static) - nocheck: if path in self.static[method]: - allowed.add(verb) + allowed.add(method) for method in set(self.dyna_regexes) - allowed - nocheck: for combined, rules in self.dyna_regexes[method]: match = combined(path) @@ -2585,7 +2585,7 @@ def parse_range_header(header, maxlen=0): def _parse_qsl(qs): r = [] - for pair in qs.replace(';','&').split('&'): + for pair in qs.split('&'): if not pair: continue nv = pair.split('=', 1) if len(nv) != 2: nv.append('') diff --git a/22-async/mojifinder/requirements.txt b/22-async/mojifinder/requirements.txt index 4c17510..4d97ea9 100644 --- a/22-async/mojifinder/requirements.txt +++ b/22-async/mojifinder/requirements.txt @@ -1,6 +1,7 @@ click==7.1.2 fastapi==0.63.0 h11==0.12.0 -pydantic==1.7.3 +pydantic==1.8.1 starlette==0.13.6 +typing-extensions==3.7.4.3 uvicorn==0.13.4 diff --git a/22-async/mojifinder/web_mojifinder.py b/22-async/mojifinder/web_mojifinder.py index 96004d8..7e0ff96 100644 --- a/22-async/mojifinder/web_mojifinder.py +++ b/22-async/mojifinder/web_mojifinder.py @@ -1,4 +1,4 @@ -import pathlib +from pathlib import Path from unicodedata import name from fastapi import FastAPI @@ -18,9 +18,8 @@ class CharName(BaseModel): # <2> def init(app): # <3> app.state.index = InvertedIndex() - static = pathlib.Path(__file__).parent.absolute() / 'static' # <4> - with open(static / 'form.html') as fp: - app.state.form = fp.read() + static = Path(__file__).parent.absolute() / 'static' # <4> + app.state.form = (static / 'form.html').read_text() init(app) # <5> @@ -33,4 +32,4 @@ async def search(q: str): # <7> def form(): # <9> return app.state.form -# no main funcion # <10> +# no main funcion # <10> \ No newline at end of file diff --git a/22-async/mojifinder/web_mojifinder_bottle.py b/22-async/mojifinder/web_mojifinder_bottle.py index 1aa0c2b..cc158a6 100755 --- a/22-async/mojifinder/web_mojifinder_bottle.py +++ b/22-async/mojifinder/web_mojifinder_bottle.py @@ -11,7 +11,7 @@ @route('/') def form(): - return static_file('form.html', root = 'static/') + return static_file('form.html', root='static/') @route('/search') @@ -28,10 +28,8 @@ def search(): def main(port): global index index = InvertedIndex() - host = 'localhost' run(host='localhost', port=port, debug=True) if __name__ == '__main__': main(8000) - From 5312d4f8246b85d9d82cc993b7fa2b962838acbf Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 24 Apr 2021 20:51:33 -0300 Subject: [PATCH 010/127] Update README.md --- 22-async/mojifinder/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/22-async/mojifinder/README.md b/22-async/mojifinder/README.md index 16b8c2e..b3df996 100644 --- a/22-async/mojifinder/README.md +++ b/22-async/mojifinder/README.md @@ -38,4 +38,4 @@ Support files: - `bottle.py`: local copy of the single-file _[Bottle](https://bottlepy.org/)_ Web framework. - `requirements.txt`: list of dependencies for `web_mojifinder.py`. - `static/form.html`: HTML form used by the `web_*` examples. -- `README.md`: this file! +- `README.md`: this file 🤓 From 1689eec623a07f4b2824d5195d9b2a34d39b36e2 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 20 May 2021 22:58:05 -0300 Subject: [PATCH 011/127] ch15: draft examples --- 15-more-types/cafeteria/cafeteria.py | 41 +++++++ 15-more-types/cafeteria/cafeteria_demo.py | 23 ++++ 15-more-types/collections_variance.py | 16 +++ {15-type-hints => 15-more-types}/erp.py | 0 {15-type-hints => 15-more-types}/erp_test.py | 4 +- 15-more-types/gen_contra.py | 59 +++++++++ 15-more-types/petbox/petbox.py | 47 ++++++++ 15-more-types/petbox/petbox_demo.py | 42 +++++++ .../randompick_generic.py | 0 .../randompick_generic_test.py | 2 +- {15-type-hints => 15-more-types}/randompop.py | 2 +- .../randompop_test.py | 2 +- 15-more-types/typeddict/books.py | 32 +++++ 15-more-types/typeddict/books_any.py | 32 +++++ 15-more-types/typeddict/demo_books.py | 20 ++++ 15-more-types/typeddict/demo_not_book.py | 23 ++++ 15-more-types/typeddict/test_books.py | 112 ++++++++++++++++++ .../typeddict/test_books_check_fails.py | 20 ++++ README.md | 60 +++++----- 19 files changed, 501 insertions(+), 36 deletions(-) create mode 100644 15-more-types/cafeteria/cafeteria.py create mode 100644 15-more-types/cafeteria/cafeteria_demo.py create mode 100644 15-more-types/collections_variance.py rename {15-type-hints => 15-more-types}/erp.py (100%) rename {15-type-hints => 15-more-types}/erp_test.py (94%) create mode 100644 15-more-types/gen_contra.py create mode 100644 15-more-types/petbox/petbox.py create mode 100644 15-more-types/petbox/petbox_demo.py rename {15-type-hints => 15-more-types}/randompick_generic.py (100%) rename {15-type-hints => 15-more-types}/randompick_generic_test.py (97%) rename {15-type-hints => 15-more-types}/randompop.py (59%) rename {15-type-hints => 15-more-types}/randompop_test.py (96%) create mode 100644 15-more-types/typeddict/books.py create mode 100644 15-more-types/typeddict/books_any.py create mode 100644 15-more-types/typeddict/demo_books.py create mode 100644 15-more-types/typeddict/demo_not_book.py create mode 100644 15-more-types/typeddict/test_books.py create mode 100644 15-more-types/typeddict/test_books_check_fails.py diff --git a/15-more-types/cafeteria/cafeteria.py b/15-more-types/cafeteria/cafeteria.py new file mode 100644 index 0000000..8c88a92 --- /dev/null +++ b/15-more-types/cafeteria/cafeteria.py @@ -0,0 +1,41 @@ +from typing import TypeVar, Generic + + +class Beverage: + """Any beverage""" + + +class Juice(Beverage): + """Any fruit juice""" + + +class OrangeJuice(Juice): + """Delicious juice Brazilian oranges""" + + +class Coak(Beverage): + """Secret formula with lots of sugar""" + + +BeverageT = TypeVar('BeverageT', bound=Beverage) +JuiceT = TypeVar('JuiceT', bound=Juice) + + +class BeverageDispenser(Generic[BeverageT]): + + beverage: BeverageT + + def __init__(self, beverage: BeverageT) -> None: + self.beverage = beverage + + def dispense(self) -> BeverageT: + return self.beverage + + +class JuiceDispenser(BeverageDispenser[JuiceT]): + pass + + +class Cafeteria: + def __init__(self, dispenser: BeverageDispenser[JuiceT]): + self.dispenser = dispenser diff --git a/15-more-types/cafeteria/cafeteria_demo.py b/15-more-types/cafeteria/cafeteria_demo.py new file mode 100644 index 0000000..d93636b --- /dev/null +++ b/15-more-types/cafeteria/cafeteria_demo.py @@ -0,0 +1,23 @@ +from cafeteria import ( + Cafeteria, + BeverageDispenser, + JuiceDispenser, + Juice, + OrangeJuice, + Coak, +) + +orange = OrangeJuice() + +orange_dispenser: JuiceDispenser[OrangeJuice] = JuiceDispenser(orange) + +juice: Juice = orange_dispenser.dispense() + +soda = Coak() + +## Value of type variable "JuiceT" of "JuiceDispenser" cannot be "Coak" +# soda_dispenser = JuiceDispenser(soda) + +soda_dispenser = BeverageDispenser(soda) + +arnold_hall = Cafeteria(soda_dispenser) diff --git a/15-more-types/collections_variance.py b/15-more-types/collections_variance.py new file mode 100644 index 0000000..12fb9dc --- /dev/null +++ b/15-more-types/collections_variance.py @@ -0,0 +1,16 @@ +from collections.abc import Collection, Sequence + +col_int: Collection[int] + +seq_int: Sequence[int] = (1, 2, 3) + +## Incompatible types in assignment +## expression has type "Collection[int]" +## variable has type "Sequence[int]" +# seq_int = col_int + +col_int = seq_int + +## List item 0 has incompatible type "float" +## expected "int" +# col_int = [1.1] diff --git a/15-type-hints/erp.py b/15-more-types/erp.py similarity index 100% rename from 15-type-hints/erp.py rename to 15-more-types/erp.py diff --git a/15-type-hints/erp_test.py b/15-more-types/erp_test.py similarity index 94% rename from 15-type-hints/erp_test.py rename to 15-more-types/erp_test.py index 5e8c067..9abdc86 100644 --- a/15-type-hints/erp_test.py +++ b/15-more-types/erp_test.py @@ -1,5 +1,4 @@ -import random -from typing import Iterable, TYPE_CHECKING, List +from typing import TYPE_CHECKING from erp import EnterpriserRandomPopper import randompop @@ -9,7 +8,6 @@ def test_issubclass() -> None: assert issubclass(EnterpriserRandomPopper, randompop.RandomPopper) - def test_isinstance_untyped_items_argument() -> None: items = [1, 2, 3] popper = EnterpriserRandomPopper(items) # [int] is not required diff --git a/15-more-types/gen_contra.py b/15-more-types/gen_contra.py new file mode 100644 index 0000000..a8ed9a9 --- /dev/null +++ b/15-more-types/gen_contra.py @@ -0,0 +1,59 @@ +""" +In ``Generator[YieldType, SendType, ReturnType]``, +``SendType`` is contravariant. +The other type variables are covariant. + +This is how ``typing.Generator`` is declared:: + + class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co]): + +(from https://docs.python.org/3/library/typing.html#typing.Generator) + +""" + +from typing import Generator + + +# Generator[YieldType, SendType, ReturnType] + +def gen_float_take_int() -> Generator[float, int, str]: + received = yield -1.0 + while received: + received = yield float(received) + return 'Done' + + +def gen_float_take_float() -> Generator[float, float, str]: + received = yield -1.0 + while received: + received = yield float(received) + return 'Done' + + +def gen_float_take_complex() -> Generator[float, complex, str]: + received = yield -1.0 + while received: + received = yield abs(received) + return 'Done' + +# Generator[YieldType, SendType, ReturnType] + +g0: Generator[float, float, str] = gen_float_take_float() + +g1: Generator[complex, float, str] = gen_float_take_float() + +## Incompatible types in assignment +## expression has type "Generator[float, float, str]" +## variable has type "Generator[int, float, str]") +# g2: Generator[int, float, str] = gen_float_take_float() + + +# Generator[YieldType, SendType, ReturnType] + +g3: Generator[float, int, str] = gen_float_take_float() + +## Incompatible types in assignment +## expression has type "Generator[float, float, str]" +## variable has type "Generator[float, complex, str]") +## g4: Generator[float, complex, str] = gen_float_take_float() + diff --git a/15-more-types/petbox/petbox.py b/15-more-types/petbox/petbox.py new file mode 100644 index 0000000..04b72ea --- /dev/null +++ b/15-more-types/petbox/petbox.py @@ -0,0 +1,47 @@ +from typing import TypeVar, Generic, Any + + +class Pet: + """Domestic animal kept for companionship.""" + + +class Dog(Pet): + """Canis familiaris""" + + +class Cat(Pet): + """Felis catus""" + + +class Siamese(Cat): + """Cat breed from Thailand""" + + +T = TypeVar('T') + + +class Box(Generic[T]): + def put(self, item: T) -> None: + self.contents = item + + def get(self) -> T: + return self.contents + + +T_contra = TypeVar('T_contra', contravariant=True) + + +class InBox(Generic[T_contra]): + def put(self, item: T) -> None: + self.contents = item + + +T_co = TypeVar('T_co', covariant=True) + + +class OutBox(Generic[T_co]): + def __init__(self, contents: Any): + self.contents = contents + + def get(self) -> Any: + return self.contents diff --git a/15-more-types/petbox/petbox_demo.py b/15-more-types/petbox/petbox_demo.py new file mode 100644 index 0000000..d09cab4 --- /dev/null +++ b/15-more-types/petbox/petbox_demo.py @@ -0,0 +1,42 @@ +from typing import TYPE_CHECKING + +from petbox import * + + +cat_box: Box[Cat] = Box() + +si = Siamese() + +cat_box.put(si) + +animal = cat_box.get() + +#if TYPE_CHECKING: +# reveal_type(animal) # Revealed: petbox.Cat* + + +################### Covariance + +out_box: OutBox[Cat] = OutBox(Cat()) + +out_box_si: OutBox[Siamese] = OutBox(Siamese()) + +## Incompatible types in assignment +## expression has type "OutBox[Cat]" +# variable has type "OutBox[Siamese]" +# out_box_si = out_box + +out_box = out_box_si + +################### Contravariance + +in_box: InBox[Cat] = InBox() + +in_box_si: InBox[Siamese] = InBox() + +in_box_si = in_box + +## Incompatible types in assignment +## expression has type "InBox[Siamese]" +## variable has type "InBox[Cat]" +# in_box = in_box_si diff --git a/15-type-hints/randompick_generic.py b/15-more-types/randompick_generic.py similarity index 100% rename from 15-type-hints/randompick_generic.py rename to 15-more-types/randompick_generic.py diff --git a/15-type-hints/randompick_generic_test.py b/15-more-types/randompick_generic_test.py similarity index 97% rename from 15-type-hints/randompick_generic_test.py rename to 15-more-types/randompick_generic_test.py index 07ebdc4..d642b26 100644 --- a/15-type-hints/randompick_generic_test.py +++ b/15-more-types/randompick_generic_test.py @@ -4,7 +4,7 @@ from randompick_generic import GenericRandomPicker -class LottoPicker(): +class LottoPicker: def __init__(self, items: Iterable[int]) -> None: self._items = list(items) random.shuffle(self._items) diff --git a/15-type-hints/randompop.py b/15-more-types/randompop.py similarity index 59% rename from 15-type-hints/randompop.py rename to 15-more-types/randompop.py index 35cad38..cf9c811 100644 --- a/15-type-hints/randompop.py +++ b/15-more-types/randompop.py @@ -1,4 +1,4 @@ -from typing import Protocol, TypeVar, runtime_checkable, Any +from typing import Protocol, runtime_checkable, Any @runtime_checkable diff --git a/15-type-hints/randompop_test.py b/15-more-types/randompop_test.py similarity index 96% rename from 15-type-hints/randompop_test.py rename to 15-more-types/randompop_test.py index 0b0f317..7cce7f6 100644 --- a/15-type-hints/randompop_test.py +++ b/15-more-types/randompop_test.py @@ -3,7 +3,7 @@ from typing import Any, Iterable, TYPE_CHECKING -class SimplePopper(): +class SimplePopper: def __init__(self, items: Iterable) -> None: self._items = list(items) random.shuffle(self._items) diff --git a/15-more-types/typeddict/books.py b/15-more-types/typeddict/books.py new file mode 100644 index 0000000..5a0bc82 --- /dev/null +++ b/15-more-types/typeddict/books.py @@ -0,0 +1,32 @@ +# tag::BOOKDICT[] +from typing import TypedDict, List +import json + +class BookDict(TypedDict): + isbn: str + title: str + authors: List[str] + pagecount: int +# end::BOOKDICT[] + +# tag::TOXML[] +AUTHOR_EL = '{}' + +def to_xml(book: BookDict) -> str: # <1> + elements: List[str] = [] # <2> + for key, value in book.items(): + if isinstance(value, list): # <3> + elements.extend( + AUTHOR_EL.format(n) for n in value) # <4> + else: + tag = key.upper() + elements.append(f'<{tag}>{value}') + xml = '\n\t'.join(elements) + return f'\n\t{xml}\n' +# end::TOXML[] + +# tag::FROMJSON[] +def from_json(data: str) -> BookDict: + whatever: BookDict = json.loads(data) # <1> + return whatever # <2> +# end::FROMJSON[] \ No newline at end of file diff --git a/15-more-types/typeddict/books_any.py b/15-more-types/typeddict/books_any.py new file mode 100644 index 0000000..49a544e --- /dev/null +++ b/15-more-types/typeddict/books_any.py @@ -0,0 +1,32 @@ +# tag::BOOKDICT[] +from typing import TypedDict, List +import json + +class BookDict(TypedDict): + isbn: str + title: str + authors: List[str] + pagecount: int +# end::BOOKDICT[] + +# tag::TOXML[] +AUTHOR_EL = '{}' + +def to_xml(book: BookDict) -> str: # <1> + elements: List[str] = [] # <2> + for key, value in book.items(): + if isinstance(value, list): # <3> + elements.extend(AUTHOR_EL.format(n) + for n in value) + else: + tag = key.upper() + elements.append(f'<{tag}>{value}') + xml = '\n\t'.join(elements) + return f'\n\t{xml}\n' +# end::TOXML[] + +# tag::FROMJSON[] +def from_json(data: str) -> BookDict: + whatever = json.loads(data) # <1> + return whatever # <2> +# end::FROMJSON[] diff --git a/15-more-types/typeddict/demo_books.py b/15-more-types/typeddict/demo_books.py new file mode 100644 index 0000000..5203acb --- /dev/null +++ b/15-more-types/typeddict/demo_books.py @@ -0,0 +1,20 @@ +from books import BookDict +from typing import TYPE_CHECKING + +def demo() -> None: # <1> + book = BookDict( # <2> + isbn='0134757599', + title='Refactoring, 2e', + authors=['Martin Fowler', 'Kent Beck'], + pagecount=478 + ) + authors = book['authors'] # <3> + if TYPE_CHECKING: # <4> + reveal_type(authors) # <5> + authors = 'Bob' # <6> + book['weight'] = 4.2 + del book['title'] + + +if __name__ == '__main__': + demo() diff --git a/15-more-types/typeddict/demo_not_book.py b/15-more-types/typeddict/demo_not_book.py new file mode 100644 index 0000000..7bf0711 --- /dev/null +++ b/15-more-types/typeddict/demo_not_book.py @@ -0,0 +1,23 @@ +from books import to_xml, from_json +from typing import TYPE_CHECKING + +def demo() -> None: + NOT_BOOK_JSON = """ + {"title": "Andromeda Strain", + "flavor": "pistachio", + "authors": true} + """ + not_book = from_json(NOT_BOOK_JSON) # <1> + if TYPE_CHECKING: # <2> + reveal_type(not_book) + reveal_type(not_book['authors']) + + print(not_book) # <3> + print(not_book['flavor']) # <4> + + xml = to_xml(not_book) # <5> + print(xml) # <6> + + +if __name__ == '__main__': + demo() diff --git a/15-more-types/typeddict/test_books.py b/15-more-types/typeddict/test_books.py new file mode 100644 index 0000000..fc9d245 --- /dev/null +++ b/15-more-types/typeddict/test_books.py @@ -0,0 +1,112 @@ +import json +from typing import cast + +from books import BookDict, to_xml, from_json + +XML_SAMPLE = """ + +\t0134757599 +\tRefactoring, 2e +\tMartin Fowler +\tKent Beck +\t478 + +""".strip() + + +# using plain dicts + +def test_1() -> None: + xml = to_xml({ + 'isbn': '0134757599', + 'title': 'Refactoring, 2e', + 'authors': ['Martin Fowler', 'Kent Beck'], + 'pagecount': 478, + }) + assert xml == XML_SAMPLE + +def test_2() -> None: + xml = to_xml(dict( + isbn='0134757599', + title='Refactoring, 2e', + authors=['Martin Fowler', 'Kent Beck'], + pagecount=478)) + assert xml == XML_SAMPLE + +def test_5() -> None: + book_data: BookDict = dict( + isbn='0134757599', + title='Refactoring, 2e', + authors=['Martin Fowler', 'Kent Beck'], + pagecount=478 + ) + xml = to_xml(book_data) + assert xml == XML_SAMPLE + +def test_6() -> None: + book_data = dict( + isbn='0134757599', + title='Refactoring, 2e', + authors=['Martin Fowler', 'Kent Beck'], + pagecount=478 + ) + xml = to_xml(cast(BookDict, book_data)) # cast needed + assert xml == XML_SAMPLE + +def test_4() -> None: + xml = to_xml(BookDict( + isbn='0134757599', + title='Refactoring, 2e', + authors=['Martin Fowler', 'Kent Beck'], + pagecount=478)) + assert xml == XML_SAMPLE + +def test_7() -> None: + book_data = BookDict( + isbn='0134757599', + title='Refactoring, 2e', + authors=['Martin Fowler', 'Kent Beck'], + pagecount=478 + ) + xml = to_xml(book_data) + assert xml == XML_SAMPLE + +def test_8() -> None: + book_data: BookDict = { + 'isbn': '0134757599', + 'title': 'Refactoring, 2e', + 'authors': ['Martin Fowler', 'Kent Beck'], + 'pagecount': 478, + } + xml = to_xml(book_data) + assert xml == XML_SAMPLE + +BOOK_JSON = """ + {"isbn": "0134757599", + "title": "Refactoring, 2e", + "authors": ["Martin Fowler", "Kent Beck"], + "pagecount": 478} +""" + +def test_load_book_0() -> None: + book_data: BookDict = json.loads(BOOK_JSON) # typed var + xml = to_xml(book_data) + assert xml == XML_SAMPLE + +def test_load_book() -> None: + book_data = from_json(BOOK_JSON) + xml = to_xml(book_data) + assert xml == XML_SAMPLE + + +NOT_BOOK_JSON = """ + {"isbn": 3.141592653589793 + "title": [1, 2, 3], + "authors": ["Martin Fowler", "Kent Beck"], + "flavor": "strawberry"} +""" + +def test_load_not_book() -> None: + book_data: BookDict = json.loads(BOOK_JSON) # typed var + xml = to_xml(book_data) + assert xml == XML_SAMPLE diff --git a/15-more-types/typeddict/test_books_check_fails.py b/15-more-types/typeddict/test_books_check_fails.py new file mode 100644 index 0000000..6166f97 --- /dev/null +++ b/15-more-types/typeddict/test_books_check_fails.py @@ -0,0 +1,20 @@ +from books import BookDict, to_xml + +XML_SAMPLE = """ + +\t0134757599 +\tRefactoring, 2e +\tMartin Fowler +\tKent Beck +\t478 + +""".strip() + +def test_3() -> None: + xml = to_xml(BookDict(dict([ # Expected keyword arguments, {...}, or dict(...) in TypedDict constructor + ('isbn', '0134757599'), + ('title', 'Refactoring, 2e'), + ('authors', ['Martin Fowler', 'Kent Beck']), + ('pagecount', 478), + ]))) + assert xml == XML_SAMPLE diff --git a/README.md b/README.md index 56e7420..d0b57b3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Fluent Python 2e example code -Example code for the book **Fluent Python, 2nd edition** by Luciano Ramalho (O'Reilly, 2021). +Example code for the book **Fluent Python, 2nd edition** by Luciano Ramalho (O'Reilly, 2020). > **BEWARE**: This is a work in progress! > @@ -14,40 +14,40 @@ Example code for the book **Fluent Python, 2nd edition** by Luciano R All chapters are undergoing review and updates, including significant rewrites in the chapters about concurrency in **Part V**. -New chapters in **Fluent Python 2nd edition** are marked with 🆕. +New chapters in **Fluent Python 2e** are marked with 🆕. -🚨 This table of contents is subject to change at any time until the book goes to the printer. +🚨 This table of contents is subject to change at any time until the book goes to the printer. -Part / Chapter #|Title|Directory|1st edition Chapter # ----:|---|---|:---: +Part / Chapter #|Title|Directory|Notebook|1st ed. Chapter # +---:|---|---|---|:---: **I – Prologue**| -1|The Python Data Model|[01-data-model](01-data-model)|1 +1|The Python Data Model|[01-data-model](01-data-model)|[data-model.ipynb](01-data-model/data-model.ipynb)|1 **II – Data Structures**| -2|An Array of Sequences|[02-array-seq](02-array-seq)|2 -3|Dictionaries and Sets|[03-dict-set](03-dict-set)|3 -4|Text versus Bytes|[04-text-byte](04-text-byte)|4 -5|Record-like Data Structures|[05-record-like](05-record-like)|🆕 -6|Object References, Mutability, and Recycling|[06-obj-ref](06-obj-ref)|8 +2|An Array of Sequences|[02-array-seq](02-array-seq)|[array-seq.ipynb](02-array-seq/array-seq.ipynb)|2 +3|Dictionaries and Sets|[03-dict-set](03-dict-set)||3 +4|Text versus Bytes|[04-text-byte](04-text-byte)||4 +🆕 5|Record-like Data Structures|[05-record-like](05-record-like)||– +6|Object References, Mutability, and Recycling|[06-obj-ref](06-obj-ref)||8 **III – Functions as Objects**| -7|First-Class Funcions|[07-1class-func](07-1class-func)|5 -8|Type Hints in Function Definitions|[08-def-type-hints](08-def-type-hints)|🆕 -9|Function Decorators and Closures|[09-closure-deco](09-closure-deco)|7 -10|Design Patterns with First-Class Functions|[10-dp-1class-func](10-dp-1class-func)|6 +7|First-Class Funcions|[07-1class-func](07-1class-func)||5 +🆕 8|Type Hints in Function Definitions|[08-def-type-hints](08-def-type-hints)||– +9|Function Decorators and Closures|[09-closure-deco](09-closure-deco)||7 +10|Design Patterns with First-Class Functions|[10-dp-1class-func](10-dp-1class-func)||6 **IV – Object-Oriented Idioms**| -11|A Pythonic Object|[11-pythonic-obj](11-pythonic-obj)|9 -12|Sequence Hacking, Hashing, and Slicing|[12-seq-hacking](12-seq-hacking)|10 -13|Interfaces, Protocols, and ABCs|[13-protocl-abc](13-protocol-abc)|11 -14|Inheritance: For Good or For Worse|[14-inheritance](14-inheritance)|12 -15|More About Type Hints|15-more-typing|🆕 -16|Operator Overloading: Doing It Right|[16-op-overloading](16-op-overloading)|13 +11|A Pythonic Object|[11-pythonic-obj](11-pythonic-obj)||9 +12|Sequence Hacking, Hashing, and Slicing|[12-seq-hacking](12-seq-hacking)||10 +13|Interfaces, Protocols, and ABCs|[13-protocl-abc](13-protocol-abc)||11 +14|Inheritance: For Good or For Worse|[14-inheritance](14-inheritance)||12 +🆕 15|More About Type Hints|[15-more-types](15-more-types)||– +16|Operator Overloading: Doing It Right|[16-op-overloading](16-op-overloading)||13 **V – Control Flow**| -17|Iterables, Iterators, and Generators|[17-it-generator](17-it-generator)|14 -18|Context Managers and else Blocks|[18-context-mngr](18-context-mngr)|15 -19|Classic Coroutines|[19-coroutine](19-coroutine)|16 -20|Concurrency Models in Python|[20-concurrency](20-concurrency)|🆕 -21|Concurrency with Futures|[21-futures](21-futures)|17 -22|Asynchronous Programming|[22-async](22-async)|18 +17|Iterables, Iterators, and Generators|[17-it-generator](17-it-generator)||14 +18|Context Managers and else Blocks|[18-context-mngr](18-context-mngr)||15 +19|Classic Coroutines|[19-coroutine](19-coroutine)||16 +🆕 20|Concurrency Models in Python|[20-concurrency](20-concurrency)||- +21|Concurrency with Futures|[21-futures](21-futures)||17 +22|Asynchronous Programming|[22-async](22-async)||18 **VI – Metaprogramming**| -23|Dynamic Attributes and Properties|[23-dyn-attr-prop](23-dyn-attr-prop)|19 -24|Attribute Descriptors|[24-descriptor](24-descriptor)|20 -25|Class Metaprogramming|[25-class-metaprog](25-class-metaprog)|21 +23|Dynamic Attributes and Properties|[22-dyn-attr-prop](22-dyn-attr-prop)||19 +24|Attribute Descriptors|[23-descriptor](23-descriptor)||20 +25|Class Metaprogramming|[24-class-metaprog](24-class-metaprog)||21 From 14e1758bf7d2da85061fe71c8175f9ba5b287a1e Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 20 May 2021 23:01:56 -0300 Subject: [PATCH 012/127] Update pydantic version (low risc vulnerability) --- 22-async/mojifinder/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/22-async/mojifinder/requirements.txt b/22-async/mojifinder/requirements.txt index 4d97ea9..04ef58d 100644 --- a/22-async/mojifinder/requirements.txt +++ b/22-async/mojifinder/requirements.txt @@ -1,7 +1,7 @@ click==7.1.2 fastapi==0.63.0 h11==0.12.0 -pydantic==1.8.1 +pydantic==1.8.2 starlette==0.13.6 typing-extensions==3.7.4.3 uvicorn==0.13.4 From eb8982f9249843d8d1c942b9fb5bfb10743697da Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 20 May 2021 23:13:16 -0300 Subject: [PATCH 013/127] added credits to petbox examples --- 15-more-types/petbox/petbox.py | 5 +++++ 15-more-types/petbox/petbox_demo.py | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/15-more-types/petbox/petbox.py b/15-more-types/petbox/petbox.py index 04b72ea..5b963f3 100644 --- a/15-more-types/petbox/petbox.py +++ b/15-more-types/petbox/petbox.py @@ -1,3 +1,8 @@ +""" +Example adapted from `Atomic Kotlin` by Bruce Eckel & Svetlana Isakova, +chapter `Creating Generics`, section `Variance`. +""" + from typing import TypeVar, Generic, Any diff --git a/15-more-types/petbox/petbox_demo.py b/15-more-types/petbox/petbox_demo.py index d09cab4..a7e8bab 100644 --- a/15-more-types/petbox/petbox_demo.py +++ b/15-more-types/petbox/petbox_demo.py @@ -1,3 +1,8 @@ +""" +Example adapted from `Atomic Kotlin` by Bruce Eckel & Svetlana Isakova, +chapter `Creating Generics`, section `Variance`. +""" + from typing import TYPE_CHECKING from petbox import * @@ -11,7 +16,7 @@ animal = cat_box.get() -#if TYPE_CHECKING: +# if TYPE_CHECKING: # reveal_type(animal) # Revealed: petbox.Cat* From fc81928f36416a21a544a793189e2879288fa4c5 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 20 May 2021 23:21:26 -0300 Subject: [PATCH 014/127] change order of experiments --- 15-more-types/petbox/petbox_demo.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/15-more-types/petbox/petbox_demo.py b/15-more-types/petbox/petbox_demo.py index a7e8bab..e73f2b7 100644 --- a/15-more-types/petbox/petbox_demo.py +++ b/15-more-types/petbox/petbox_demo.py @@ -26,22 +26,22 @@ out_box_si: OutBox[Siamese] = OutBox(Siamese()) +out_box = out_box_si + ## Incompatible types in assignment ## expression has type "OutBox[Cat]" -# variable has type "OutBox[Siamese]" +## variable has type "OutBox[Siamese]" # out_box_si = out_box -out_box = out_box_si - ################### Contravariance in_box: InBox[Cat] = InBox() in_box_si: InBox[Siamese] = InBox() -in_box_si = in_box - ## Incompatible types in assignment ## expression has type "InBox[Siamese]" ## variable has type "InBox[Cat]" # in_box = in_box_si + +in_box_si = in_box From ff5fdd8f7c22c95a4fe79731ce5146c7e8af98ca Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Fri, 21 May 2021 00:26:43 -0300 Subject: [PATCH 015/127] added cafeteria example --- 15-more-types/cafeteria.py | 82 +++++++++++++++++++++++ 15-more-types/cafeteria/cafeteria.py | 41 ------------ 15-more-types/cafeteria/cafeteria_demo.py | 23 ------- 15-more-types/petbox/petbox.py | 4 -- 4 files changed, 82 insertions(+), 68 deletions(-) create mode 100644 15-more-types/cafeteria.py delete mode 100644 15-more-types/cafeteria/cafeteria.py delete mode 100644 15-more-types/cafeteria/cafeteria_demo.py diff --git a/15-more-types/cafeteria.py b/15-more-types/cafeteria.py new file mode 100644 index 0000000..4a9fad9 --- /dev/null +++ b/15-more-types/cafeteria.py @@ -0,0 +1,82 @@ +from typing import TypeVar, Generic + + +class Beverage: + """Any beverage""" + + +class Juice(Beverage): + """Any fruit juice""" + + +class OrangeJuice(Juice): + """Delicious juice from Brazilian oranges""" + + +BeverageT = TypeVar('BeverageT', covariant=True) + + +class BeverageDispenser(Generic[BeverageT]): + def __init__(self, beverage: BeverageT) -> None: + self.beverage = beverage + + def dispense(self) -> BeverageT: + return self.beverage + + +class Garbage: + """Any garbage.""" + + +class Biodegradable(Garbage): + """Biodegradable garbage.""" + + +class Compostable(Biodegradable): + """Compostable garbage.""" + + +GarbageT = TypeVar('GarbageT', contravariant=True) + + +class TrashCan(Generic[GarbageT]): + def put(self, trash) -> None: + """Store trash until dumped...""" + + +class Cafeteria: + def __init__( + self, + dispenser: BeverageDispenser[Juice], + trash_can: TrashCan[Biodegradable] + ): + """Initialize...""" + + +beverage_dispenser = BeverageDispenser(Beverage()) +juice_dispenser = BeverageDispenser(Juice()) +orange_juice_dispenser = BeverageDispenser(OrangeJuice()) + +trash_can: TrashCan[Garbage] = TrashCan() +bio_can: TrashCan[Biodegradable] = TrashCan() +compost_can: TrashCan[Compostable] = TrashCan() + +arnold_hall = Cafeteria(juice_dispenser, bio_can) + +######################## covariance on 1st argument +arnold_hall = Cafeteria(orange_juice_dispenser, trash_can) + +## Argument 1 to "Cafeteria" has +## incompatible type "BeverageDispenser[Beverage]" +## expected "BeverageDispenser[Juice]" +# arnold_hall = Cafeteria(beverage_dispenser, trash_can) + + +######################## contravariance on 2nd argument + +## Argument 2 to "Cafeteria" has +## incompatible type "TrashCan[Compostable]" +## expected "TrashCan[Biodegradable]" +# arnold_hall = Cafeteria(juice_dispenser, compost_can) + +arnold_hall = Cafeteria(juice_dispenser, trash_can) diff --git a/15-more-types/cafeteria/cafeteria.py b/15-more-types/cafeteria/cafeteria.py deleted file mode 100644 index 8c88a92..0000000 --- a/15-more-types/cafeteria/cafeteria.py +++ /dev/null @@ -1,41 +0,0 @@ -from typing import TypeVar, Generic - - -class Beverage: - """Any beverage""" - - -class Juice(Beverage): - """Any fruit juice""" - - -class OrangeJuice(Juice): - """Delicious juice Brazilian oranges""" - - -class Coak(Beverage): - """Secret formula with lots of sugar""" - - -BeverageT = TypeVar('BeverageT', bound=Beverage) -JuiceT = TypeVar('JuiceT', bound=Juice) - - -class BeverageDispenser(Generic[BeverageT]): - - beverage: BeverageT - - def __init__(self, beverage: BeverageT) -> None: - self.beverage = beverage - - def dispense(self) -> BeverageT: - return self.beverage - - -class JuiceDispenser(BeverageDispenser[JuiceT]): - pass - - -class Cafeteria: - def __init__(self, dispenser: BeverageDispenser[JuiceT]): - self.dispenser = dispenser diff --git a/15-more-types/cafeteria/cafeteria_demo.py b/15-more-types/cafeteria/cafeteria_demo.py deleted file mode 100644 index d93636b..0000000 --- a/15-more-types/cafeteria/cafeteria_demo.py +++ /dev/null @@ -1,23 +0,0 @@ -from cafeteria import ( - Cafeteria, - BeverageDispenser, - JuiceDispenser, - Juice, - OrangeJuice, - Coak, -) - -orange = OrangeJuice() - -orange_dispenser: JuiceDispenser[OrangeJuice] = JuiceDispenser(orange) - -juice: Juice = orange_dispenser.dispense() - -soda = Coak() - -## Value of type variable "JuiceT" of "JuiceDispenser" cannot be "Coak" -# soda_dispenser = JuiceDispenser(soda) - -soda_dispenser = BeverageDispenser(soda) - -arnold_hall = Cafeteria(soda_dispenser) diff --git a/15-more-types/petbox/petbox.py b/15-more-types/petbox/petbox.py index 5b963f3..a344a96 100644 --- a/15-more-types/petbox/petbox.py +++ b/15-more-types/petbox/petbox.py @@ -10,10 +10,6 @@ class Pet: """Domestic animal kept for companionship.""" -class Dog(Pet): - """Canis familiaris""" - - class Cat(Pet): """Felis catus""" From 8a330d822b997c7992d0b7675c82ad75832300c6 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Fri, 21 May 2021 18:56:12 -0300 Subject: [PATCH 016/127] updade from Atlas repo --- 03-dict-set/dialcodes.py | 2 +- 03-dict-set/index.py | 6 +- 03-dict-set/index0.py | 4 +- 03-dict-set/index_default.py | 8 +- 04-text-byte/categories.py | 6 +- 04-text-byte/default_encodings.py | 3 +- 04-text-byte/zwj_sample.py | 6 +- 05-record-like/dataclass/coordinates.py | 3 +- 08-def-type-hints/colors.py | 1 - 08-def-type-hints/columnize2.py | 2 - 08-def-type-hints/columnize_alias.py | 1 - 08-def-type-hints/comparable/mymax_test.py | 2 +- 08-def-type-hints/double/double_test.py | 2 +- 08-def-type-hints/mode/mode_T.py | 3 +- 08-def-type-hints/passdrill.py | 9 +- 08-def-type-hints/replacer2.py | 2 +- 08-def-type-hints/romans.py | 4 +- 08-def-type-hints/typeddict/books.py | 32 -- 08-def-type-hints/typeddict/books_any.py | 32 -- 08-def-type-hints/typeddict/demo_books.py | 20 - 08-def-type-hints/typeddict/demo_not_book.py | 23 - 08-def-type-hints/typeddict/test_books.py | 112 ----- .../typeddict/test_books_check_fails.py | 20 - 09-closure-deco/clockdeco_cls.py | 1 - 09-closure-deco/registration.py | 4 +- .../monkeytype/classic_strategy_test.py | 3 +- 10-dp-1class-func/untyped/strategy_best3.py | 1 - 12-seq-hacking/vector_v1.py | 4 +- 12-seq-hacking/vector_v2.py | 4 +- 12-seq-hacking/vector_v3.py | 10 +- 12-seq-hacking/vector_v4.py | 8 +- 12-seq-hacking/vector_v5.py | 10 +- 13-protocol-abc/double/double_test.py | 2 +- 13-protocol-abc/tombola.py | 1 - 13-protocol-abc/typing/randompick_test.py | 2 +- 13-protocol-abc/typing/randompickload.py | 2 +- 13-protocol-abc/typing/randompickload_test.py | 6 +- 15-more-types/cafeteria.py | 57 ++- 15-more-types/petbox/petbox.py | 16 +- 16-op-overloading/bingoaddable.py | 21 +- 16-op-overloading/tombola.py | 3 +- 16-op-overloading/vector_py3_5.py | 431 ------------------ 16-op-overloading/vector_v6.py | 10 +- 16-op-overloading/vector_v7.py | 10 +- 16-op-overloading/vector_v8.py | 10 +- 17-it-generator/aritprog_float_error.py | 2 +- 17-it-generator/columnize_iter.py | 2 +- 17-it-generator/fibo_by_hand.py | 3 +- 17-it-generator/sentence_gen2.py | 4 +- 17-it-generator/sentence_genexp.py | 6 +- 17-it-generator/sentence_iter.py | 6 +- 17-it-generator/sentence_iter2.py | 4 +- 17-it-generator/tree/extra/pretty_tree.py | 8 +- .../tree/extra/test_pretty_tree.py | 11 +- 17-it-generator/tree/extra/test_tree.py | 12 +- 17-it-generator/tree/extra/tree.py | 2 +- 17-it-generator/tree/step0/test_tree.py | 2 +- 17-it-generator/tree/step0/tree.py | 2 +- 17-it-generator/tree/step1/test_tree.py | 4 +- 17-it-generator/tree/step2/test_tree.py | 4 +- 17-it-generator/tree/step3/test_tree.py | 6 +- 17-it-generator/tree/step4/test_tree.py | 11 +- 17-it-generator/tree/step5/test_tree.py | 13 +- 17-it-generator/tree/step6/test_tree.py | 13 +- 19-coroutine/coroutil.py | 4 +- 19-coroutine/taxi_sim.py | 12 +- 19-coroutine/taxi_sim0.py | 10 +- 21-futures/getflags/requirements.txt | 2 +- 22-async/domains/curio/domainlib.py | 7 +- 23-dyn-attr-prop/oscon/schedule_v2.py | 2 +- 23-dyn-attr-prop/oscon/schedule_v3.py | 2 +- 23-dyn-attr-prop/oscon/schedule_v4_hasattr.py | 2 +- 24-descriptor/bulkfood/bulkfood_v4.py | 2 +- 24-descriptor/bulkfood/model_v5.py | 2 +- 24-descriptor/descriptorkinds.py | 10 +- 25-class-metaprog/autoconst/autoconst.py | 22 + 25-class-metaprog/autoconst/autoconst_demo.py | 55 +++ 25-class-metaprog/bulkfood/README.md | 34 ++ 25-class-metaprog/bulkfood/bulkfood_v6.py | 84 ++++ 25-class-metaprog/bulkfood/bulkfood_v7.py | 79 ++++ 25-class-metaprog/bulkfood/bulkfood_v8.py | 86 ++++ 25-class-metaprog/bulkfood/model_v6.py | 60 +++ 25-class-metaprog/bulkfood/model_v7.py | 66 +++ 25-class-metaprog/bulkfood/model_v8.py | 80 ++++ .../decorator}/checkeddeco.py | 119 ++--- .../checked/decorator/checkeddeco_demo.py | 26 ++ .../decorator}/checkeddeco_test.py | 10 + .../checked_demo.py} | 8 +- .../checked/{ => initsub}/checkedlib.py | 68 ++- .../checked/initsub/checkedlib_test.py | 59 +++ .../metaclass/checked_demo.py} | 19 +- .../checked/metaclass/checkedlib.py | 148 ++++++ .../{ => metaclass}/checkedlib_test.py | 23 +- 25-class-metaprog/evalsupport.py | 29 -- 25-class-metaprog/evaltime.py | 49 -- 25-class-metaprog/evaltime/builderlib.py | 50 ++ 25-class-metaprog/evaltime/evaldemo.py | 30 ++ 25-class-metaprog/evaltime/evaldemo_meta.py | 33 ++ 25-class-metaprog/evaltime/metalib.py | 43 ++ 25-class-metaprog/evaltime_meta.py | 53 --- 25-class-metaprog/factories.py | 53 ++- 25-class-metaprog/factories_ducktyped.py | 79 ++++ 25-class-metaprog/hours/hours.py | 115 +++++ 25-class-metaprog/hours/hours_test.py | 71 +++ 25-class-metaprog/metabunch/README.md | 20 + 25-class-metaprog/metabunch/from3.6/bunch.py | 77 ++++ .../metabunch/from3.6/bunch_test.py | 59 +++ .../metabunch/nutshell3e/bunch.py | 85 ++++ .../metabunch/nutshell3e/bunch_test.py | 38 ++ 25-class-metaprog/metabunch/original/bunch.py | 70 +++ .../metabunch/original/bunch_test.py | 38 ++ 25-class-metaprog/metabunch/pre3.6/bunch.py | 41 ++ .../metabunch/pre3.6/bunch_test.py | 38 ++ 25-class-metaprog/persistent/persistlib.py | 59 +-- 25-class-metaprog/qualname/fakedjango.py | 5 + 25-class-metaprog/qualname/models.py | 13 + 25-class-metaprog/setattr/example_from_leo.py | 19 + 25-class-metaprog/slots/slots_timing.py | 48 ++ 25-class-metaprog/tinyenums/microenum.py | 16 +- pytest.ini | 2 + 120 files changed, 2187 insertions(+), 1181 deletions(-) delete mode 100644 08-def-type-hints/typeddict/books.py delete mode 100644 08-def-type-hints/typeddict/books_any.py delete mode 100644 08-def-type-hints/typeddict/demo_books.py delete mode 100644 08-def-type-hints/typeddict/demo_not_book.py delete mode 100644 08-def-type-hints/typeddict/test_books.py delete mode 100644 08-def-type-hints/typeddict/test_books_check_fails.py delete mode 100644 16-op-overloading/vector_py3_5.py create mode 100644 25-class-metaprog/autoconst/autoconst.py create mode 100755 25-class-metaprog/autoconst/autoconst_demo.py create mode 100644 25-class-metaprog/bulkfood/README.md create mode 100644 25-class-metaprog/bulkfood/bulkfood_v6.py create mode 100644 25-class-metaprog/bulkfood/bulkfood_v7.py create mode 100644 25-class-metaprog/bulkfood/bulkfood_v8.py create mode 100644 25-class-metaprog/bulkfood/model_v6.py create mode 100644 25-class-metaprog/bulkfood/model_v7.py create mode 100644 25-class-metaprog/bulkfood/model_v8.py rename 25-class-metaprog/{checkeddeco => checked/decorator}/checkeddeco.py (52%) create mode 100755 25-class-metaprog/checked/decorator/checkeddeco_demo.py rename 25-class-metaprog/{checkeddeco => checked/decorator}/checkeddeco_test.py (79%) rename 25-class-metaprog/checked/{checkedlib_demo.py => initsub/checked_demo.py} (65%) mode change 100644 => 100755 rename 25-class-metaprog/checked/{ => initsub}/checkedlib.py (66%) create mode 100644 25-class-metaprog/checked/initsub/checkedlib_test.py rename 25-class-metaprog/{checkeddeco/checkeddeco_demo.py => checked/metaclass/checked_demo.py} (52%) mode change 100644 => 100755 create mode 100644 25-class-metaprog/checked/metaclass/checkedlib.py rename 25-class-metaprog/checked/{ => metaclass}/checkedlib_test.py (58%) delete mode 100644 25-class-metaprog/evalsupport.py delete mode 100644 25-class-metaprog/evaltime.py create mode 100644 25-class-metaprog/evaltime/builderlib.py create mode 100755 25-class-metaprog/evaltime/evaldemo.py create mode 100755 25-class-metaprog/evaltime/evaldemo_meta.py create mode 100644 25-class-metaprog/evaltime/metalib.py delete mode 100644 25-class-metaprog/evaltime_meta.py create mode 100644 25-class-metaprog/factories_ducktyped.py create mode 100644 25-class-metaprog/hours/hours.py create mode 100644 25-class-metaprog/hours/hours_test.py create mode 100644 25-class-metaprog/metabunch/README.md create mode 100644 25-class-metaprog/metabunch/from3.6/bunch.py create mode 100644 25-class-metaprog/metabunch/from3.6/bunch_test.py create mode 100644 25-class-metaprog/metabunch/nutshell3e/bunch.py create mode 100644 25-class-metaprog/metabunch/nutshell3e/bunch_test.py create mode 100644 25-class-metaprog/metabunch/original/bunch.py create mode 100644 25-class-metaprog/metabunch/original/bunch_test.py create mode 100644 25-class-metaprog/metabunch/pre3.6/bunch.py create mode 100644 25-class-metaprog/metabunch/pre3.6/bunch_test.py create mode 100644 25-class-metaprog/qualname/fakedjango.py create mode 100755 25-class-metaprog/qualname/models.py create mode 100755 25-class-metaprog/setattr/example_from_leo.py create mode 100755 25-class-metaprog/slots/slots_timing.py create mode 100644 pytest.ini diff --git a/03-dict-set/dialcodes.py b/03-dict-set/dialcodes.py index 07b06a7..7130e08 100644 --- a/03-dict-set/dialcodes.py +++ b/03-dict-set/dialcodes.py @@ -17,7 +17,7 @@ print('d1:', d1.keys()) d2 = dict(sorted(DIAL_CODES)) # <2> print('d2:', d2.keys()) -d3 = dict(sorted(DIAL_CODES, key=lambda x:x[1])) # <3> +d3 = dict(sorted(DIAL_CODES, key=lambda x: x[1])) # <3> print('d3:', d3.keys()) assert d1 == d2 and d2 == d3 # <4> # end::DIALCODES[] diff --git a/03-dict-set/index.py b/03-dict-set/index.py index 5da48b1..116a0f8 100644 --- a/03-dict-set/index.py +++ b/03-dict-set/index.py @@ -5,8 +5,8 @@ # tag::INDEX[] """Build an index mapping word -> list of occurrences""" -import sys import re +import sys WORD_RE = re.compile(r'\w+') @@ -15,11 +15,11 @@ for line_no, line in enumerate(fp, 1): for match in WORD_RE.finditer(line): word = match.group() - column_no = match.start()+1 + column_no = match.start() + 1 location = (line_no, column_no) index.setdefault(word, []).append(location) # <1> -# print in alphabetical order +# display in alphabetical order for word in sorted(index, key=str.upper): print(word, index[word]) # end::INDEX[] diff --git a/03-dict-set/index0.py b/03-dict-set/index0.py index 0bc9bf4..bd69ab2 100644 --- a/03-dict-set/index0.py +++ b/03-dict-set/index0.py @@ -5,8 +5,8 @@ # tag::INDEX0[] """Build an index mapping word -> list of occurrences""" -import sys import re +import sys WORD_RE = re.compile(r'\w+') @@ -22,7 +22,7 @@ occurrences.append(location) # <2> index[word] = occurrences # <3> -# print in alphabetical order +# display in alphabetical order for word in sorted(index, key=str.upper): # <4> print(word, index[word]) # end::INDEX0[] diff --git a/03-dict-set/index_default.py b/03-dict-set/index_default.py index 60279a9..d4203d2 100644 --- a/03-dict-set/index_default.py +++ b/03-dict-set/index_default.py @@ -5,9 +5,9 @@ # tag::INDEX_DEFAULT[] """Build an index mapping word -> list of occurrences""" -import sys -import re import collections +import re +import sys WORD_RE = re.compile(r'\w+') @@ -16,11 +16,11 @@ for line_no, line in enumerate(fp, 1): for match in WORD_RE.finditer(line): word = match.group() - column_no = match.start()+1 + column_no = match.start() + 1 location = (line_no, column_no) index[word].append(location) # <2> -# print in alphabetical order +# display in alphabetical order for word in sorted(index, key=str.upper): print(word, index[word]) # end::INDEX_DEFAULT[] diff --git a/04-text-byte/categories.py b/04-text-byte/categories.py index a4cecbe..11cc28e 100644 --- a/04-text-byte/categories.py +++ b/04-text-byte/categories.py @@ -1,6 +1,6 @@ import sys import collections -from unicodedata import name, category +from unicodedata import category def category_stats(): @@ -19,7 +19,7 @@ def category_scan(desired): for code in range(sys.maxunicode + 1): char = chr(code) if category(char) == desired: - yield char + yield char def main(args): @@ -30,7 +30,7 @@ def main(args): count += 1 if count > 200: break - print() + print() print(count, 'characters shown') else: counts, firsts = category_stats() diff --git a/04-text-byte/default_encodings.py b/04-text-byte/default_encodings.py index c230dea..4ff70a6 100644 --- a/04-text-byte/default_encodings.py +++ b/04-text-byte/default_encodings.py @@ -1,4 +1,5 @@ -import sys, locale +import locale +import sys expressions = """ locale.getpreferredencoding() diff --git a/04-text-byte/zwj_sample.py b/04-text-byte/zwj_sample.py index cdd7a30..28893cc 100644 --- a/04-text-byte/zwj_sample.py +++ b/04-text-byte/zwj_sample.py @@ -10,9 +10,9 @@ 1F469 200D 2764 FE0F 200D 1F48B 200D 1F469 |kiss: woman, woman |E2.0 """ -markers = {'\u200D': 'ZWG', # ZERO WIDTH JOINER - '\uFE0F': 'V16', # VARIATION SELECTOR-16 - } +markers = {'\u200D': 'ZWG', # ZERO WIDTH JOINER + '\uFE0F': 'V16', # VARIATION SELECTOR-16 + } for line in zwg_sample.strip().split('\n'): code, descr, version = (s.strip() for s in line.split('|')) diff --git a/05-record-like/dataclass/coordinates.py b/05-record-like/dataclass/coordinates.py index 7bcd7cd..baacb6e 100644 --- a/05-record-like/dataclass/coordinates.py +++ b/05-record-like/dataclass/coordinates.py @@ -13,7 +13,6 @@ @dataclass(frozen=True) class Coordinate: - lat: float long: float @@ -21,4 +20,4 @@ def __str__(self): ns = 'N' if self.lat >= 0 else 'S' we = 'E' if self.long >= 0 else 'W' return f'{abs(self.lat):.1f}°{ns}, {abs(self.long):.1f}°{we}' -# end::COORDINATE[] \ No newline at end of file +# end::COORDINATE[] diff --git a/08-def-type-hints/colors.py b/08-def-type-hints/colors.py index 36518d6..b57413c 100644 --- a/08-def-type-hints/colors.py +++ b/08-def-type-hints/colors.py @@ -76,6 +76,5 @@ def demo(): print(n, name2hex(n, o)) - if __name__ == '__main__': demo() diff --git a/08-def-type-hints/columnize2.py b/08-def-type-hints/columnize2.py index 67b5fdd..2524fd3 100644 --- a/08-def-type-hints/columnize2.py +++ b/08-def-type-hints/columnize2.py @@ -10,7 +10,6 @@ def columnize(sequence: Sequence[T], num_columns: int = 0) -> List[Tuple[T, ...] return [tuple(sequence[i::num_rows]) for i in range(num_rows)] - def demo() -> None: nato = ('Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India' ' Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo' @@ -22,7 +21,6 @@ def demo() -> None: print(f'{word:15}', end='') print() - print() for length in range(2, 21, 6): values = list(range(1, length + 1)) diff --git a/08-def-type-hints/columnize_alias.py b/08-def-type-hints/columnize_alias.py index 95b85b3..b783469 100644 --- a/08-def-type-hints/columnize_alias.py +++ b/08-def-type-hints/columnize_alias.py @@ -10,7 +10,6 @@ def columnize(sequence: Sequence[str], num_columns: int) -> List[Row]: return [tuple(sequence[i::num_rows]) for i in range(num_rows)] - def demo() -> None: nato = ('Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India' ' Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo' diff --git a/08-def-type-hints/comparable/mymax_test.py b/08-def-type-hints/comparable/mymax_test.py index 3434364..a169232 100644 --- a/08-def-type-hints/comparable/mymax_test.py +++ b/08-def-type-hints/comparable/mymax_test.py @@ -1,4 +1,4 @@ -from typing import List, Callable, TypeVar +from typing import List, Callable import pytest # type: ignore diff --git a/08-def-type-hints/double/double_test.py b/08-def-type-hints/double/double_test.py index d37aba5..1871a1a 100644 --- a/08-def-type-hints/double/double_test.py +++ b/08-def-type-hints/double/double_test.py @@ -53,4 +53,4 @@ def test_double_nparray() -> None: def test_double_none() -> None: given = None with pytest.raises(TypeError): - result = double(given) + double(given) diff --git a/08-def-type-hints/mode/mode_T.py b/08-def-type-hints/mode/mode_T.py index ca26d7b..cf88bbb 100644 --- a/08-def-type-hints/mode/mode_T.py +++ b/08-def-type-hints/mode/mode_T.py @@ -12,7 +12,7 @@ def mode(data: Iterable[T]) -> T: def demo() -> None: - from typing import List, Set, TYPE_CHECKING + from typing import TYPE_CHECKING pop: list[set] = [set(), set()] m = mode(pop) if TYPE_CHECKING: @@ -21,5 +21,6 @@ def demo() -> None: print(pop) print(repr(m), type(m)) + if __name__ == '__main__': demo() diff --git a/08-def-type-hints/passdrill.py b/08-def-type-hints/passdrill.py index 08d1ff0..83b1be1 100755 --- a/08-def-type-hints/passdrill.py +++ b/08-def-type-hints/passdrill.py @@ -3,12 +3,11 @@ """passdrill: typing drills for practicing passphrases """ -import sys import os +import sys +from base64 import b64encode, b64decode from getpass import getpass from hashlib import scrypt -from base64 import b64encode, b64decode - from typing import Sequence, Tuple HASH_FILENAME = 'passdrill.hash' @@ -20,7 +19,7 @@ def prompt() -> str: confirmed = '' while confirmed != 'y': passphrase = input('Type passphrase to hash (it will be echoed): ') - if passphrase == '' or passphrase == 'q': + if passphrase in ('', 'q'): print('ERROR: the passphrase cannot be empty or "q".') continue print(f'Passphrase to be hashed -> {passphrase}') @@ -45,7 +44,7 @@ def save_hash() -> None: salted_hash = build_hash(prompt()) with open(HASH_FILENAME, 'wb') as fp: fp.write(salted_hash) - print(f'Passphrase hash saved to', HASH_FILENAME) + print(f'Passphrase hash saved to {HASH_FILENAME}') def load_hash() -> Tuple[bytes, bytes]: diff --git a/08-def-type-hints/replacer2.py b/08-def-type-hints/replacer2.py index 786c8d7..33cdaeb 100644 --- a/08-def-type-hints/replacer2.py +++ b/08-def-type-hints/replacer2.py @@ -20,7 +20,7 @@ class FromTo(NamedTuple): to: str -def zip_replace(text: str, changes: Iterable[FromTo], count:int = -1) -> str: +def zip_replace(text: str, changes: Iterable[FromTo], count: int = -1) -> str: for from_, to in changes: text = text.replace(from_, to, count) return text diff --git a/08-def-type-hints/romans.py b/08-def-type-hints/romans.py index a8bd6b6..ab23957 100644 --- a/08-def-type-hints/romans.py +++ b/08-def-type-hints/romans.py @@ -1,6 +1,6 @@ values_map = [ - (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1), - ( 'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV','I') + (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1), + ( 'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I') ] def to_roman(arabic: int) -> str: diff --git a/08-def-type-hints/typeddict/books.py b/08-def-type-hints/typeddict/books.py deleted file mode 100644 index 5a0bc82..0000000 --- a/08-def-type-hints/typeddict/books.py +++ /dev/null @@ -1,32 +0,0 @@ -# tag::BOOKDICT[] -from typing import TypedDict, List -import json - -class BookDict(TypedDict): - isbn: str - title: str - authors: List[str] - pagecount: int -# end::BOOKDICT[] - -# tag::TOXML[] -AUTHOR_EL = '{}' - -def to_xml(book: BookDict) -> str: # <1> - elements: List[str] = [] # <2> - for key, value in book.items(): - if isinstance(value, list): # <3> - elements.extend( - AUTHOR_EL.format(n) for n in value) # <4> - else: - tag = key.upper() - elements.append(f'<{tag}>{value}') - xml = '\n\t'.join(elements) - return f'\n\t{xml}\n' -# end::TOXML[] - -# tag::FROMJSON[] -def from_json(data: str) -> BookDict: - whatever: BookDict = json.loads(data) # <1> - return whatever # <2> -# end::FROMJSON[] \ No newline at end of file diff --git a/08-def-type-hints/typeddict/books_any.py b/08-def-type-hints/typeddict/books_any.py deleted file mode 100644 index e696ff3..0000000 --- a/08-def-type-hints/typeddict/books_any.py +++ /dev/null @@ -1,32 +0,0 @@ -# tag::BOOKDICT[] -from typing import TypedDict, List -import json - -class BookDict(TypedDict): - isbn: str - title: str - authors: List[str] - pagecount: int -# end::BOOKDICT[] - -# tag::TOXML[] -AUTHOR_EL = '{}' - -def to_xml(book: BookDict) -> str: # <1> - elements: List[str] = [] # <2> - for key, value in book.items(): - if isinstance(value, list): # <3> - elements.extend(AUTHOR_EL.format(n) - for n in value) - else: - tag = key.upper() - elements.append(f'<{tag}>{value}') - xml = '\n\t'.join(elements) - return f'\n\t{xml}\n' -# end::TOXML[] - -# tag::FROMJSON[] -def from_json(data: str) -> BookDict: - whatever = json.loads(data) # <1> - return whatever # <2> -# end::FROMJSON[] \ No newline at end of file diff --git a/08-def-type-hints/typeddict/demo_books.py b/08-def-type-hints/typeddict/demo_books.py deleted file mode 100644 index 5203acb..0000000 --- a/08-def-type-hints/typeddict/demo_books.py +++ /dev/null @@ -1,20 +0,0 @@ -from books import BookDict -from typing import TYPE_CHECKING - -def demo() -> None: # <1> - book = BookDict( # <2> - isbn='0134757599', - title='Refactoring, 2e', - authors=['Martin Fowler', 'Kent Beck'], - pagecount=478 - ) - authors = book['authors'] # <3> - if TYPE_CHECKING: # <4> - reveal_type(authors) # <5> - authors = 'Bob' # <6> - book['weight'] = 4.2 - del book['title'] - - -if __name__ == '__main__': - demo() diff --git a/08-def-type-hints/typeddict/demo_not_book.py b/08-def-type-hints/typeddict/demo_not_book.py deleted file mode 100644 index 7bf0711..0000000 --- a/08-def-type-hints/typeddict/demo_not_book.py +++ /dev/null @@ -1,23 +0,0 @@ -from books import to_xml, from_json -from typing import TYPE_CHECKING - -def demo() -> None: - NOT_BOOK_JSON = """ - {"title": "Andromeda Strain", - "flavor": "pistachio", - "authors": true} - """ - not_book = from_json(NOT_BOOK_JSON) # <1> - if TYPE_CHECKING: # <2> - reveal_type(not_book) - reveal_type(not_book['authors']) - - print(not_book) # <3> - print(not_book['flavor']) # <4> - - xml = to_xml(not_book) # <5> - print(xml) # <6> - - -if __name__ == '__main__': - demo() diff --git a/08-def-type-hints/typeddict/test_books.py b/08-def-type-hints/typeddict/test_books.py deleted file mode 100644 index fc9d245..0000000 --- a/08-def-type-hints/typeddict/test_books.py +++ /dev/null @@ -1,112 +0,0 @@ -import json -from typing import cast - -from books import BookDict, to_xml, from_json - -XML_SAMPLE = """ - -\t0134757599 -\tRefactoring, 2e -\tMartin Fowler -\tKent Beck -\t478 - -""".strip() - - -# using plain dicts - -def test_1() -> None: - xml = to_xml({ - 'isbn': '0134757599', - 'title': 'Refactoring, 2e', - 'authors': ['Martin Fowler', 'Kent Beck'], - 'pagecount': 478, - }) - assert xml == XML_SAMPLE - -def test_2() -> None: - xml = to_xml(dict( - isbn='0134757599', - title='Refactoring, 2e', - authors=['Martin Fowler', 'Kent Beck'], - pagecount=478)) - assert xml == XML_SAMPLE - -def test_5() -> None: - book_data: BookDict = dict( - isbn='0134757599', - title='Refactoring, 2e', - authors=['Martin Fowler', 'Kent Beck'], - pagecount=478 - ) - xml = to_xml(book_data) - assert xml == XML_SAMPLE - -def test_6() -> None: - book_data = dict( - isbn='0134757599', - title='Refactoring, 2e', - authors=['Martin Fowler', 'Kent Beck'], - pagecount=478 - ) - xml = to_xml(cast(BookDict, book_data)) # cast needed - assert xml == XML_SAMPLE - -def test_4() -> None: - xml = to_xml(BookDict( - isbn='0134757599', - title='Refactoring, 2e', - authors=['Martin Fowler', 'Kent Beck'], - pagecount=478)) - assert xml == XML_SAMPLE - -def test_7() -> None: - book_data = BookDict( - isbn='0134757599', - title='Refactoring, 2e', - authors=['Martin Fowler', 'Kent Beck'], - pagecount=478 - ) - xml = to_xml(book_data) - assert xml == XML_SAMPLE - -def test_8() -> None: - book_data: BookDict = { - 'isbn': '0134757599', - 'title': 'Refactoring, 2e', - 'authors': ['Martin Fowler', 'Kent Beck'], - 'pagecount': 478, - } - xml = to_xml(book_data) - assert xml == XML_SAMPLE - -BOOK_JSON = """ - {"isbn": "0134757599", - "title": "Refactoring, 2e", - "authors": ["Martin Fowler", "Kent Beck"], - "pagecount": 478} -""" - -def test_load_book_0() -> None: - book_data: BookDict = json.loads(BOOK_JSON) # typed var - xml = to_xml(book_data) - assert xml == XML_SAMPLE - -def test_load_book() -> None: - book_data = from_json(BOOK_JSON) - xml = to_xml(book_data) - assert xml == XML_SAMPLE - - -NOT_BOOK_JSON = """ - {"isbn": 3.141592653589793 - "title": [1, 2, 3], - "authors": ["Martin Fowler", "Kent Beck"], - "flavor": "strawberry"} -""" - -def test_load_not_book() -> None: - book_data: BookDict = json.loads(BOOK_JSON) # typed var - xml = to_xml(book_data) - assert xml == XML_SAMPLE diff --git a/08-def-type-hints/typeddict/test_books_check_fails.py b/08-def-type-hints/typeddict/test_books_check_fails.py deleted file mode 100644 index 6166f97..0000000 --- a/08-def-type-hints/typeddict/test_books_check_fails.py +++ /dev/null @@ -1,20 +0,0 @@ -from books import BookDict, to_xml - -XML_SAMPLE = """ - -\t0134757599 -\tRefactoring, 2e -\tMartin Fowler -\tKent Beck -\t478 - -""".strip() - -def test_3() -> None: - xml = to_xml(BookDict(dict([ # Expected keyword arguments, {...}, or dict(...) in TypedDict constructor - ('isbn', '0134757599'), - ('title', 'Refactoring, 2e'), - ('authors', ['Martin Fowler', 'Kent Beck']), - ('pagecount', 478), - ]))) - assert xml == XML_SAMPLE diff --git a/09-closure-deco/clockdeco_cls.py b/09-closure-deco/clockdeco_cls.py index edb1ac4..4400006 100644 --- a/09-closure-deco/clockdeco_cls.py +++ b/09-closure-deco/clockdeco_cls.py @@ -41,4 +41,3 @@ def snooze(seconds): for i in range(3): snooze(.123) - diff --git a/09-closure-deco/registration.py b/09-closure-deco/registration.py index 855b6da..f1a8a0b 100644 --- a/09-closure-deco/registration.py +++ b/09-closure-deco/registration.py @@ -25,7 +25,7 @@ def main(): # <8> f2() f3() -if __name__=='__main__': +if __name__ == '__main__': main() # <9> -# end::REGISTRATION[] \ No newline at end of file +# end::REGISTRATION[] diff --git a/10-dp-1class-func/monkeytype/classic_strategy_test.py b/10-dp-1class-func/monkeytype/classic_strategy_test.py index b730962..6f62091 100644 --- a/10-dp-1class-func/monkeytype/classic_strategy_test.py +++ b/10-dp-1class-func/monkeytype/classic_strategy_test.py @@ -56,8 +56,7 @@ def test_large_order_promo_no_discount(customer_fidelity_0, cart_plain) -> None: def test_large_order_promo_with_discount(customer_fidelity_0) -> None: - cart = [LineItem(str(item_code), 1, 1.0) - for item_code in range(10)] + cart = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)] order = Order(customer_fidelity_0, cart, LargeOrderPromo()) assert order.total() == 10.0 assert order.due() == 9.3 diff --git a/10-dp-1class-func/untyped/strategy_best3.py b/10-dp-1class-func/untyped/strategy_best3.py index de6ce4d..8d21ffc 100644 --- a/10-dp-1class-func/untyped/strategy_best3.py +++ b/10-dp-1class-func/untyped/strategy_best3.py @@ -89,4 +89,3 @@ def best_promo(order): # end::STRATEGY_BEST3[] - diff --git a/12-seq-hacking/vector_v1.py b/12-seq-hacking/vector_v1.py index 5f75c04..9d734b6 100644 --- a/12-seq-hacking/vector_v1.py +++ b/12-seq-hacking/vector_v1.py @@ -100,7 +100,7 @@ def __iter__(self): def __repr__(self): components = reprlib.repr(self._components) # <3> components = components[components.find('['):-1] # <4> - return 'Vector({})'.format(components) + return f'Vector({components})' def __str__(self): return str(tuple(self)) @@ -113,7 +113,7 @@ def __eq__(self, other): return tuple(self) == tuple(other) def __abs__(self): - return math.sqrt(sum(x * x for x in self)) # <6> + return math.hypot(*self) # <6> def __bool__(self): return bool(abs(self)) diff --git a/12-seq-hacking/vector_v2.py b/12-seq-hacking/vector_v2.py index 23d98ee..f25c243 100644 --- a/12-seq-hacking/vector_v2.py +++ b/12-seq-hacking/vector_v2.py @@ -127,7 +127,7 @@ def __iter__(self): def __repr__(self): components = reprlib.repr(self._components) components = components[components.find('['):-1] - return 'Vector({})'.format(components) + return f'Vector({components})' def __str__(self): return str(tuple(self)) @@ -140,7 +140,7 @@ def __eq__(self, other): return tuple(self) == tuple(other) def __abs__(self): - return math.sqrt(sum(x * x for x in self)) + return math.hypot(*self) def __bool__(self): return bool(abs(self)) diff --git a/12-seq-hacking/vector_v3.py b/12-seq-hacking/vector_v3.py index ea4ec52..6ee18b5 100644 --- a/12-seq-hacking/vector_v3.py +++ b/12-seq-hacking/vector_v3.py @@ -170,7 +170,7 @@ def __iter__(self): def __repr__(self): components = reprlib.repr(self._components) components = components[components.find('['):-1] - return 'Vector({})'.format(components) + return f'Vector({components})' def __str__(self): return str(tuple(self)) @@ -183,7 +183,7 @@ def __eq__(self, other): return tuple(self) == tuple(other) def __abs__(self): - return math.sqrt(sum(x * x for x in self)) + return math.hypot(*self) def __bool__(self): return bool(abs(self)) @@ -207,8 +207,8 @@ def __getattr__(self, name): pos = cls.shortcut_names.find(name) # <3> if 0 <= pos < len(self._components): # <4> return self._components[pos] - msg = '{.__name__!r} object has no attribute {!r}' # <5> - raise AttributeError(msg.format(cls, name)) + msg = f'{cls.__name__!r} object has no attribute {name!r}' # <5> + raise AttributeError(msg) # end::VECTOR_V3_GETATTR[] # tag::VECTOR_V3_SETATTR[] @@ -216,7 +216,7 @@ def __setattr__(self, name, value): cls = type(self) if len(name) == 1: # <1> if name in cls.shortcut_names: # <2> - error = 'readonly attribute {attr_name!r}' + error = 'read-only attribute {attr_name!r}' elif name.islower(): # <3> error = "can't set attributes 'a' to 'z' in {cls_name!r}" else: diff --git a/12-seq-hacking/vector_v4.py b/12-seq-hacking/vector_v4.py index 211a076..95530eb 100644 --- a/12-seq-hacking/vector_v4.py +++ b/12-seq-hacking/vector_v4.py @@ -166,7 +166,7 @@ def __iter__(self): def __repr__(self): components = reprlib.repr(self._components) components = components[components.find('['):-1] - return 'Vector({})'.format(components) + return f'Vector({components})' def __str__(self): return str(tuple(self)) @@ -184,7 +184,7 @@ def __hash__(self): return functools.reduce(operator.xor, hashes, 0) def __abs__(self): - return math.sqrt(sum(x * x for x in self)) + return math.hypot(*self) def __bool__(self): return bool(abs(self)) @@ -207,8 +207,8 @@ def __getattr__(self, name): pos = cls.shortcut_names.find(name) if 0 <= pos < len(self._components): return self._components[pos] - msg = '{.__name__!r} object has no attribute {!r}' - raise AttributeError(msg.format(cls, name)) + msg = f'{cls.__name__!r} object has no attribute {name!r}' + raise AttributeError(msg) @classmethod def frombytes(cls, octets): diff --git a/12-seq-hacking/vector_v5.py b/12-seq-hacking/vector_v5.py index c5c7344..ebbd523 100644 --- a/12-seq-hacking/vector_v5.py +++ b/12-seq-hacking/vector_v5.py @@ -209,7 +209,7 @@ def __iter__(self): def __repr__(self): components = reprlib.repr(self._components) components = components[components.find('['):-1] - return 'Vector({})'.format(components) + return f'Vector({components})' def __str__(self): return str(tuple(self)) @@ -227,7 +227,7 @@ def __hash__(self): return functools.reduce(operator.xor, hashes, 0) def __abs__(self): - return math.sqrt(sum(x * x for x in self)) + return math.hypot(*self) def __bool__(self): return bool(abs(self)) @@ -250,11 +250,11 @@ def __getattr__(self, name): pos = cls.shortcut_names.find(name) if 0 <= pos < len(self._components): return self._components[pos] - msg = '{.__name__!r} object has no attribute {!r}' - raise AttributeError(msg.format(cls, name)) + msg = f'{cls.__name__!r} object has no attribute {name!r}' + raise AttributeError(msg) def angle(self, n): # <2> - r = math.sqrt(sum(x * x for x in self[n:])) + r = math.hypot(*self[n:]) a = math.atan2(r, self[n-1]) if (n == len(self) - 1) and (self[-1] < 0): return math.pi * 2 - a diff --git a/13-protocol-abc/double/double_test.py b/13-protocol-abc/double/double_test.py index d37aba5..1871a1a 100644 --- a/13-protocol-abc/double/double_test.py +++ b/13-protocol-abc/double/double_test.py @@ -53,4 +53,4 @@ def test_double_nparray() -> None: def test_double_none() -> None: given = None with pytest.raises(TypeError): - result = double(given) + double(given) diff --git a/13-protocol-abc/tombola.py b/13-protocol-abc/tombola.py index ea6cb78..6f4c6cd 100644 --- a/13-protocol-abc/tombola.py +++ b/13-protocol-abc/tombola.py @@ -19,7 +19,6 @@ def loaded(self): # <4> """Return `True` if there's at least 1 item, `False` otherwise.""" return bool(self.inspect()) # <5> - def inspect(self): """Return a sorted tuple with the items currently inside.""" items = [] diff --git a/13-protocol-abc/typing/randompick_test.py b/13-protocol-abc/typing/randompick_test.py index 957441c..f090022 100644 --- a/13-protocol-abc/typing/randompick_test.py +++ b/13-protocol-abc/typing/randompick_test.py @@ -3,7 +3,7 @@ from randompick import RandomPicker # <1> -class SimplePicker(): # <2> +class SimplePicker: # <2> def __init__(self, items: Iterable) -> None: self._items = list(items) random.shuffle(self._items) diff --git a/13-protocol-abc/typing/randompickload.py b/13-protocol-abc/typing/randompickload.py index 7357915..88d93a8 100644 --- a/13-protocol-abc/typing/randompickload.py +++ b/13-protocol-abc/typing/randompickload.py @@ -1,4 +1,4 @@ -from typing import Protocol, runtime_checkable, Any, Iterable +from typing import Protocol, runtime_checkable from randompick import RandomPicker @runtime_checkable # <1> diff --git a/13-protocol-abc/typing/randompickload_test.py b/13-protocol-abc/typing/randompickload_test.py index 0bef0db..ef4d049 100644 --- a/13-protocol-abc/typing/randompickload_test.py +++ b/13-protocol-abc/typing/randompickload_test.py @@ -1,9 +1,9 @@ import random -from typing import Any, Iterable, TYPE_CHECKING +from typing import Any, Iterable from randompickload import LoadableRandomPicker -class SimplePicker(): +class SimplePicker: def __init__(self, items: Iterable) -> None: self._items = list(items) random.shuffle(self._items) @@ -11,7 +11,7 @@ def __init__(self, items: Iterable) -> None: def pick(self) -> Any: return self._items.pop() -class LoadablePicker(): # <1> +class LoadablePicker: # <1> def __init__(self, items: Iterable) -> None: self.load(items) diff --git a/15-more-types/cafeteria.py b/15-more-types/cafeteria.py index 4a9fad9..622f8e4 100644 --- a/15-more-types/cafeteria.py +++ b/15-more-types/cafeteria.py @@ -2,25 +2,25 @@ class Beverage: - """Any beverage""" + """Any beverage.""" class Juice(Beverage): - """Any fruit juice""" + """Any fruit juice.""" class OrangeJuice(Juice): - """Delicious juice from Brazilian oranges""" + """Delicious juice from Brazilian oranges.""" -BeverageT = TypeVar('BeverageT', covariant=True) +T_co = TypeVar('T_co', covariant=True) -class BeverageDispenser(Generic[BeverageT]): - def __init__(self, beverage: BeverageT) -> None: +class BeverageDispenser(Generic[T_co]): + def __init__(self, beverage: T_co) -> None: self.beverage = beverage - def dispense(self) -> BeverageT: + def dispense(self) -> T_co: return self.beverage @@ -36,11 +36,11 @@ class Compostable(Biodegradable): """Compostable garbage.""" -GarbageT = TypeVar('GarbageT', contravariant=True) +T_contra = TypeVar('T_contra', contravariant=True) -class TrashCan(Generic[GarbageT]): - def put(self, trash) -> None: +class TrashCan(Generic[T_contra]): + def put(self, trash: T_contra) -> None: """Store trash until dumped...""" @@ -48,35 +48,48 @@ class Cafeteria: def __init__( self, dispenser: BeverageDispenser[Juice], - trash_can: TrashCan[Biodegradable] + trash_can: TrashCan[Biodegradable], ): """Initialize...""" -beverage_dispenser = BeverageDispenser(Beverage()) -juice_dispenser = BeverageDispenser(Juice()) -orange_juice_dispenser = BeverageDispenser(OrangeJuice()) +################################################ exact types -trash_can: TrashCan[Garbage] = TrashCan() +juice_dispenser = BeverageDispenser(Juice()) bio_can: TrashCan[Biodegradable] = TrashCan() -compost_can: TrashCan[Compostable] = TrashCan() arnold_hall = Cafeteria(juice_dispenser, bio_can) -######################## covariance on 1st argument -arnold_hall = Cafeteria(orange_juice_dispenser, trash_can) + +################################################ covariant dispenser + +orange_juice_dispenser = BeverageDispenser(OrangeJuice()) + +arnold_hall = Cafeteria(orange_juice_dispenser, bio_can) + + +################################################ non-covariant dispenser + +beverage_dispenser = BeverageDispenser(Beverage()) ## Argument 1 to "Cafeteria" has ## incompatible type "BeverageDispenser[Beverage]" ## expected "BeverageDispenser[Juice]" -# arnold_hall = Cafeteria(beverage_dispenser, trash_can) +# arnold_hall = Cafeteria(beverage_dispenser, bio_can) + + +################################################ contravariant trash + +trash_can: TrashCan[Garbage] = TrashCan() +arnold_hall = Cafeteria(juice_dispenser, trash_can) + + +################################################ non-contravariant trash -######################## contravariance on 2nd argument +compost_can: TrashCan[Compostable] = TrashCan() ## Argument 2 to "Cafeteria" has ## incompatible type "TrashCan[Compostable]" ## expected "TrashCan[Biodegradable]" # arnold_hall = Cafeteria(juice_dispenser, compost_can) - -arnold_hall = Cafeteria(juice_dispenser, trash_can) diff --git a/15-more-types/petbox/petbox.py b/15-more-types/petbox/petbox.py index a344a96..6ecd86d 100644 --- a/15-more-types/petbox/petbox.py +++ b/15-more-types/petbox/petbox.py @@ -29,14 +29,6 @@ def get(self) -> T: return self.contents -T_contra = TypeVar('T_contra', contravariant=True) - - -class InBox(Generic[T_contra]): - def put(self, item: T) -> None: - self.contents = item - - T_co = TypeVar('T_co', covariant=True) @@ -46,3 +38,11 @@ def __init__(self, contents: Any): def get(self) -> Any: return self.contents + + +T_contra = TypeVar('T_contra', contravariant=True) + + +class InBox(Generic[T_contra]): + def put(self, item: T) -> None: + self.contents = item diff --git a/16-op-overloading/bingoaddable.py b/16-op-overloading/bingoaddable.py index 20294a4..38a70c7 100644 --- a/16-op-overloading/bingoaddable.py +++ b/16-op-overloading/bingoaddable.py @@ -53,34 +53,29 @@ """ # tag::ADDABLE_BINGO[] -import itertools # <1> - from tombola import Tombola from bingo import BingoCage -class AddableBingoCage(BingoCage): # <2> +class AddableBingoCage(BingoCage): # <1> def __add__(self, other): - if isinstance(other, Tombola): # <3> - return AddableBingoCage(self.inspect() + other.inspect()) + if isinstance(other, Tombola): # <2> + return AddableBingoCage(self.inspect() + other.inspect()) else: return NotImplemented def __iadd__(self, other): if isinstance(other, Tombola): - other_iterable = other.inspect() # <4> + other_iterable = other.inspect() # <3> else: try: - other_iterable = iter(other) # <5> - except TypeError: # <6> + other_iterable = iter(other) # <4> + except TypeError: # <5> self_cls = type(self).__name__ msg = "right operand in += must be {!r} or an iterable" raise TypeError(msg.format(self_cls)) - self.load(other_iterable) # <7> - return self # <8> - - - + self.load(other_iterable) # <6> + return self # <7> # end::ADDABLE_BINGO[] diff --git a/16-op-overloading/tombola.py b/16-op-overloading/tombola.py index 5ed0f85..b38d075 100644 --- a/16-op-overloading/tombola.py +++ b/16-op-overloading/tombola.py @@ -19,7 +19,6 @@ def loaded(self): # <4> """Return `True` if there's at least 1 item, `False` otherwise.""" return bool(self.inspect()) # <5> - def inspect(self): """Return a sorted tuple with the items currently inside.""" items = [] @@ -31,5 +30,5 @@ def inspect(self): self.load(items) # <7> return tuple(sorted(items)) - # END TOMBOLA_ABC + diff --git a/16-op-overloading/vector_py3_5.py b/16-op-overloading/vector_py3_5.py deleted file mode 100644 index ad04864..0000000 --- a/16-op-overloading/vector_py3_5.py +++ /dev/null @@ -1,431 +0,0 @@ -""" -A multi-dimensional ``Vector`` class, take 9: operator ``@`` - -WARNING: This example requires Python 3.5 or later. - -A ``Vector`` is built from an iterable of numbers:: - - >>> Vector([3.1, 4.2]) - Vector([3.1, 4.2]) - >>> Vector((3, 4, 5)) - Vector([3.0, 4.0, 5.0]) - >>> Vector(range(10)) - Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...]) - - -Tests with 2-dimensions (same results as ``vector2d_v1.py``):: - - >>> v1 = Vector([3, 4]) - >>> x, y = v1 - >>> x, y - (3.0, 4.0) - >>> v1 - Vector([3.0, 4.0]) - >>> v1_clone = eval(repr(v1)) - >>> v1 == v1_clone - True - >>> print(v1) - (3.0, 4.0) - >>> octets = bytes(v1) - >>> octets - b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@' - >>> abs(v1) - 5.0 - >>> bool(v1), bool(Vector([0, 0])) - (True, False) - - -Test of ``.frombytes()`` class method: - - >>> v1_clone = Vector.frombytes(bytes(v1)) - >>> v1_clone - Vector([3.0, 4.0]) - >>> v1 == v1_clone - True - - -Tests with 3-dimensions:: - - >>> v1 = Vector([3, 4, 5]) - >>> x, y, z = v1 - >>> x, y, z - (3.0, 4.0, 5.0) - >>> v1 - Vector([3.0, 4.0, 5.0]) - >>> v1_clone = eval(repr(v1)) - >>> v1 == v1_clone - True - >>> print(v1) - (3.0, 4.0, 5.0) - >>> abs(v1) # doctest:+ELLIPSIS - 7.071067811... - >>> bool(v1), bool(Vector([0, 0, 0])) - (True, False) - - -Tests with many dimensions:: - - >>> v7 = Vector(range(7)) - >>> v7 - Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...]) - >>> abs(v7) # doctest:+ELLIPSIS - 9.53939201... - - -Test of ``.__bytes__`` and ``.frombytes()`` methods:: - - >>> v1 = Vector([3, 4, 5]) - >>> v1_clone = Vector.frombytes(bytes(v1)) - >>> v1_clone - Vector([3.0, 4.0, 5.0]) - >>> v1 == v1_clone - True - - -Tests of sequence behavior:: - - >>> v1 = Vector([3, 4, 5]) - >>> len(v1) - 3 - >>> v1[0], v1[len(v1)-1], v1[-1] - (3.0, 5.0, 5.0) - - -Test of slicing:: - - >>> v7 = Vector(range(7)) - >>> v7[-1] - 6.0 - >>> v7[1:4] - Vector([1.0, 2.0, 3.0]) - >>> v7[-1:] - Vector([6.0]) - >>> v7[1,2] - Traceback (most recent call last): - ... - TypeError: Vector indices must be integers - - -Tests of dynamic attribute access:: - - >>> v7 = Vector(range(10)) - >>> v7.x - 0.0 - >>> v7.y, v7.z, v7.t - (1.0, 2.0, 3.0) - -Dynamic attribute lookup failures:: - - >>> v7.k - Traceback (most recent call last): - ... - AttributeError: 'Vector' object has no attribute 'k' - >>> v3 = Vector(range(3)) - >>> v3.t - Traceback (most recent call last): - ... - AttributeError: 'Vector' object has no attribute 't' - >>> v3.spam - Traceback (most recent call last): - ... - AttributeError: 'Vector' object has no attribute 'spam' - - -Tests of hashing:: - - >>> v1 = Vector([3, 4]) - >>> v2 = Vector([3.1, 4.2]) - >>> v3 = Vector([3, 4, 5]) - >>> v6 = Vector(range(6)) - >>> hash(v1), hash(v3), hash(v6) - (7, 2, 1) - - -Most hash codes of non-integers vary from a 32-bit to 64-bit Python build:: - - >>> import sys - >>> hash(v2) == (384307168202284039 if sys.maxsize > 2**32 else 357915986) - True - - -Tests of ``format()`` with Cartesian coordinates in 2D:: - - >>> v1 = Vector([3, 4]) - >>> format(v1) - '(3.0, 4.0)' - >>> format(v1, '.2f') - '(3.00, 4.00)' - >>> format(v1, '.3e') - '(3.000e+00, 4.000e+00)' - - -Tests of ``format()`` with Cartesian coordinates in 3D and 7D:: - - >>> v3 = Vector([3, 4, 5]) - >>> format(v3) - '(3.0, 4.0, 5.0)' - >>> format(Vector(range(7))) - '(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0)' - - -Tests of ``format()`` with spherical coordinates in 2D, 3D and 4D:: - - >>> format(Vector([1, 1]), 'h') # doctest:+ELLIPSIS - '<1.414213..., 0.785398...>' - >>> format(Vector([1, 1]), '.3eh') - '<1.414e+00, 7.854e-01>' - >>> format(Vector([1, 1]), '0.5fh') - '<1.41421, 0.78540>' - >>> format(Vector([1, 1, 1]), 'h') # doctest:+ELLIPSIS - '<1.73205..., 0.95531..., 0.78539...>' - >>> format(Vector([2, 2, 2]), '.3eh') - '<3.464e+00, 9.553e-01, 7.854e-01>' - >>> format(Vector([0, 0, 0]), '0.5fh') - '<0.00000, 0.00000, 0.00000>' - >>> format(Vector([-1, -1, -1, -1]), 'h') # doctest:+ELLIPSIS - '<2.0, 2.09439..., 2.18627..., 3.92699...>' - >>> format(Vector([2, 2, 2, 2]), '.3eh') - '<4.000e+00, 1.047e+00, 9.553e-01, 7.854e-01>' - >>> format(Vector([0, 1, 0, 0]), '0.5fh') - '<1.00000, 1.57080, 0.00000, 0.00000>' - - -Basic tests of operator ``+``:: - - >>> v1 = Vector([3, 4, 5]) - >>> v2 = Vector([6, 7, 8]) - >>> v1 + v2 - Vector([9.0, 11.0, 13.0]) - >>> v1 + v2 == Vector([3+6, 4+7, 5+8]) - True - >>> v3 = Vector([1, 2]) - >>> v1 + v3 # short vectors are filled with 0.0 on addition - Vector([4.0, 6.0, 5.0]) - - -Tests of ``+`` with mixed types:: - - >>> v1 + (10, 20, 30) - Vector([13.0, 24.0, 35.0]) - >>> from vector2d_v3 import Vector2d - >>> v2d = Vector2d(1, 2) - >>> v1 + v2d - Vector([4.0, 6.0, 5.0]) - - -Tests of ``+`` with mixed types, swapped operands:: - - >>> (10, 20, 30) + v1 - Vector([13.0, 24.0, 35.0]) - >>> from vector2d_v3 import Vector2d - >>> v2d = Vector2d(1, 2) - >>> v2d + v1 - Vector([4.0, 6.0, 5.0]) - - -Tests of ``+`` with an unsuitable operand: - - >>> v1 + 1 - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for +: 'Vector' and 'int' - >>> v1 + 'ABC' - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for +: 'Vector' and 'str' - - -Basic tests of operator ``*``:: - - >>> v1 = Vector([1, 2, 3]) - >>> v1 * 10 - Vector([10.0, 20.0, 30.0]) - >>> 10 * v1 - Vector([10.0, 20.0, 30.0]) - - -Tests of ``*`` with unusual but valid operands:: - - >>> v1 * True - Vector([1.0, 2.0, 3.0]) - >>> from fractions import Fraction - >>> v1 * Fraction(1, 3) # doctest:+ELLIPSIS - Vector([0.3333..., 0.6666..., 1.0]) - - -Tests of ``*`` with unsuitable operands:: - - >>> v1 * (1, 2) - Traceback (most recent call last): - ... - TypeError: can't multiply sequence by non-int of type 'Vector' - - -Tests of operator `==`:: - - >>> va = Vector(range(1, 4)) - >>> vb = Vector([1.0, 2.0, 3.0]) - >>> va == vb - True - >>> vc = Vector([1, 2]) - >>> from vector2d_v3 import Vector2d - >>> v2d = Vector2d(1, 2) - >>> vc == v2d - True - >>> va == (1, 2, 3) - False - - -Tests of operator `!=`:: - - >>> va != vb - False - >>> vc != v2d - False - >>> va != (1, 2, 3) - True - - -Tests for operator `@` (Python >= 3.5), computing the dot product:: - - >>> va = Vector([1, 2, 3]) - >>> vz = Vector([5, 6, 7]) - >>> va @ vz == 38.0 # 1*5 + 2*6 + 3*7 - True - >>> [10, 20, 30] @ vz - 380.0 - >>> va @ 3 - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for @: 'Vector' and 'int' - - -""" - -from array import array -import reprlib -import math -import functools -import operator -import itertools -import numbers - - -class Vector: - typecode = 'd' - - def __init__(self, components): - self._components = array(self.typecode, components) - - def __iter__(self): - return iter(self._components) - - def __repr__(self): - components = reprlib.repr(self._components) - components = components[components.find('['):-1] - return 'Vector({})'.format(components) - - def __str__(self): - return str(tuple(self)) - - def __bytes__(self): - return (bytes([ord(self.typecode)]) + - bytes(self._components)) - - def __eq__(self, other): - if isinstance(other, Vector): - return (len(self) == len(other) and - all(a == b for a, b in zip(self, other))) - else: - return NotImplemented - - def __hash__(self): - hashes = (hash(x) for x in self) - return functools.reduce(operator.xor, hashes, 0) - - def __abs__(self): - return math.sqrt(sum(x * x for x in self)) - - def __bool__(self): - return bool(abs(self)) - - def __len__(self): - return len(self._components) - - def __getitem__(self, index): - cls = type(self) - if isinstance(index, slice): - return cls(self._components[index]) - elif isinstance(index, int): - return self._components[index] - else: - msg = '{.__name__} indices must be integers' - raise TypeError(msg.format(cls)) - - shortcut_names = 'xyzt' - - def __getattr__(self, name): - cls = type(self) - if len(name) == 1: - pos = cls.shortcut_names.find(name) - if 0 <= pos < len(self._components): - return self._components[pos] - msg = '{.__name__!r} object has no attribute {!r}' - raise AttributeError(msg.format(cls, name)) - - def angle(self, n): - r = math.sqrt(sum(x * x for x in self[n:])) - a = math.atan2(r, self[n-1]) - if (n == len(self) - 1) and (self[-1] < 0): - return math.pi * 2 - a - else: - return a - - def angles(self): - return (self.angle(n) for n in range(1, len(self))) - - def __format__(self, fmt_spec=''): - if fmt_spec.endswith('h'): # hyperspherical coordinates - fmt_spec = fmt_spec[:-1] - coords = itertools.chain([abs(self)], - self.angles()) - outer_fmt = '<{}>' - else: - coords = self - outer_fmt = '({})' - components = (format(c, fmt_spec) for c in coords) - return outer_fmt.format(', '.join(components)) - - @classmethod - def frombytes(cls, octets): - typecode = chr(octets[0]) - memv = memoryview(octets[1:]).cast(typecode) - return cls(memv) - - def __add__(self, other): - try: - pairs = itertools.zip_longest(self, other, fillvalue=0.0) - return Vector(a + b for a, b in pairs) - except TypeError: - return NotImplemented - - def __radd__(self, other): - return self + other - - def __mul__(self, scalar): - if isinstance(scalar, numbers.Real): - return Vector(n * scalar for n in self) - else: - return NotImplemented - - def __rmul__(self, scalar): - return self * scalar - - def __matmul__(self, other): - try: - return sum(a * b for a, b in zip(self, other)) - except TypeError: - return NotImplemented - - def __rmatmul__(self, other): - return self @ other # this only works in Python 3.5 diff --git a/16-op-overloading/vector_v6.py b/16-op-overloading/vector_v6.py index a272a9b..7154851 100644 --- a/16-op-overloading/vector_v6.py +++ b/16-op-overloading/vector_v6.py @@ -264,7 +264,7 @@ def __iter__(self): def __repr__(self): components = reprlib.repr(self._components) components = components[components.find('['):-1] - return 'Vector({})'.format(components) + return f'Vector({components})' def __str__(self): return str(tuple(self)) @@ -283,7 +283,7 @@ def __hash__(self): # tag::VECTOR_V6_UNARY[] def __abs__(self): - return math.sqrt(sum(x * x for x in self)) + return math.hypot(*self) def __neg__(self): return Vector(-x for x in self) # <1> @@ -313,11 +313,11 @@ def __getattr__(self, name): pos = cls.shortcut_names.find(name) if 0 <= pos < len(self._components): return self._components[pos] - msg = '{.__name__!r} object has no attribute {!r}' - raise AttributeError(msg.format(cls, name)) + msg = f'{cls.__name__!r} object has no attribute {name!r}' + raise AttributeError(msg) def angle(self, n): - r = math.sqrt(sum(x * x for x in self[n:])) + r = math.hypot(*self[n:]) a = math.atan2(r, self[n-1]) if (n == len(self) - 1) and (self[-1] < 0): return math.pi * 2 - a diff --git a/16-op-overloading/vector_v7.py b/16-op-overloading/vector_v7.py index 241956e..e59c896 100644 --- a/16-op-overloading/vector_v7.py +++ b/16-op-overloading/vector_v7.py @@ -316,7 +316,7 @@ def __iter__(self): def __repr__(self): components = reprlib.repr(self._components) components = components[components.find('['):-1] - return 'Vector({})'.format(components) + return f'Vector({components})' def __str__(self): return str(tuple(self)) @@ -334,7 +334,7 @@ def __hash__(self): return functools.reduce(operator.xor, hashes, 0) def __abs__(self): - return math.sqrt(sum(x * x for x in self)) + return math.hypot(*self) def __neg__(self): return Vector(-x for x in self) @@ -363,11 +363,11 @@ def __getattr__(self, name): pos = cls.shortcut_names.find(name) if 0 <= pos < len(self._components): return self._components[pos] - msg = '{.__name__!r} object has no attribute {!r}' - raise AttributeError(msg.format(cls, name)) + msg = f'{cls.__name__!r} object has no attribute {name!r}' + raise AttributeError(msg) def angle(self, n): - r = math.sqrt(sum(x * x for x in self[n:])) + r = math.hypot(*self[n:]) a = math.atan2(r, self[n-1]) if (n == len(self) - 1) and (self[-1] < 0): return math.pi * 2 - a diff --git a/16-op-overloading/vector_v8.py b/16-op-overloading/vector_v8.py index 48b4cc5..ee2cb48 100644 --- a/16-op-overloading/vector_v8.py +++ b/16-op-overloading/vector_v8.py @@ -317,7 +317,7 @@ def __iter__(self): def __repr__(self): components = reprlib.repr(self._components) components = components[components.find('['):-1] - return 'Vector({})'.format(components) + return f'Vector({components})' def __str__(self): return str(tuple(self)) @@ -340,7 +340,7 @@ def __hash__(self): return functools.reduce(operator.xor, hashes, 0) def __abs__(self): - return math.sqrt(sum(x * x for x in self)) + return math.hypot(*self) def __neg__(self): return Vector(-x for x in self) @@ -369,11 +369,11 @@ def __getattr__(self, name): pos = cls.shortcut_names.find(name) if 0 <= pos < len(self._components): return self._components[pos] - msg = '{.__name__!r} object has no attribute {!r}' - raise AttributeError(msg.format(cls, name)) + msg = f'{cls.__name__!r} object has no attribute {name!r}' + raise AttributeError(msg) def angle(self, n): - r = math.sqrt(sum(x * x for x in self[n:])) + r = math.hypot(*self[n:]) a = math.atan2(r, self[n-1]) if (n == len(self) - 1) and (self[-1] < 0): return math.pi * 2 - a diff --git a/17-it-generator/aritprog_float_error.py b/17-it-generator/aritprog_float_error.py index b871ad8..9a0e7cc 100644 --- a/17-it-generator/aritprog_float_error.py +++ b/17-it-generator/aritprog_float_error.py @@ -20,7 +20,7 @@ while abs(delta) <= epsilon: delta = next(ap0) - next(ap1) frac = next(ap_frac) - iteration +=1 + iteration += 1 print('iteration: {}\tfraction: {}\tepsilon: {}\tdelta: {}'. format(iteration, frac, epsilon, delta)) diff --git a/17-it-generator/columnize_iter.py b/17-it-generator/columnize_iter.py index 867b677..c9a6f7d 100644 --- a/17-it-generator/columnize_iter.py +++ b/17-it-generator/columnize_iter.py @@ -1,5 +1,5 @@ # tag::COLUMNIZE[] -from typing import Sequence, Tuple, Iterator +from typing import Sequence, Tuple, Iterator def columnize(sequence: Sequence[str], num_columns: int = 0) -> Iterator[Tuple[str, ...]]: if num_columns == 0: diff --git a/17-it-generator/fibo_by_hand.py b/17-it-generator/fibo_by_hand.py index 9bf8ab4..4fa41c5 100644 --- a/17-it-generator/fibo_by_hand.py +++ b/17-it-generator/fibo_by_hand.py @@ -44,8 +44,9 @@ def fibonacci(): if __name__ == '__main__': for x, y in zip(Fibonacci(), fibonacci()): - assert x == y, '%s != %s' % (x, y) + assert x == y, f'{x} != {y}' print(x) if x > 10**10: break print('etc...') + diff --git a/17-it-generator/sentence_gen2.py b/17-it-generator/sentence_gen2.py index 503fecd..f362ffc 100644 --- a/17-it-generator/sentence_gen2.py +++ b/17-it-generator/sentence_gen2.py @@ -6,7 +6,7 @@ import re import reprlib -RE_WORD = re.compile('r\w+') +RE_WORD = re.compile(r'\w+') class Sentence: @@ -15,7 +15,7 @@ def __init__(self, text): self.text = text # <1> def __repr__(self): - return 'Sentence(%s)' % reprlib.repr(self.text) + return f'Sentence({reprlib.repr(self.text)})' def __iter__(self): for match in RE_WORD.finditer(self.text): # <2> diff --git a/17-it-generator/sentence_genexp.py b/17-it-generator/sentence_genexp.py index b75e859..cb9366a 100644 --- a/17-it-generator/sentence_genexp.py +++ b/17-it-generator/sentence_genexp.py @@ -15,7 +15,7 @@ def __init__(self, text): self.text = text def __repr__(self): - return 'Sentence(%s)' % reprlib.repr(self.text) + return f'Sentence({reprlib.repr(self.text)})' def __iter__(self): return (match.group() for match in RE_WORD.finditer(self.text)) @@ -29,7 +29,7 @@ def main(): filename = sys.argv[1] word_number = int(sys.argv[2]) except (IndexError, ValueError): - print('Usage: %s ' % sys.argv[0]) + print(f'Usage: {sys.argv[0]} ') sys.exit(2) # command line usage error with open(filename, 'rt', encoding='utf-8') as text_file: s = Sentence(text_file.read()) @@ -38,7 +38,7 @@ def main(): print(word) break else: - warnings.warn('last word is #%d, "%s"' % (n, word)) + warnings.warn(f'last word is #{n}, {word!r}') if __name__ == '__main__': main() diff --git a/17-it-generator/sentence_iter.py b/17-it-generator/sentence_iter.py index 5472179..0c5f225 100644 --- a/17-it-generator/sentence_iter.py +++ b/17-it-generator/sentence_iter.py @@ -19,7 +19,7 @@ def __init__(self, text): self.words = RE_WORD.findall(text) def __repr__(self): - return 'Sentence(%s)' % reprlib.repr(self.text) + return f'Sentence({reprlib.repr(self.text)})' def __iter__(self): # <1> return SentenceIterator(self.words) # <2> @@ -50,7 +50,7 @@ def main(): filename = sys.argv[1] word_number = int(sys.argv[2]) except (IndexError, ValueError): - print('Usage: %s ' % sys.argv[0]) + print(f'Usage: {sys.argv[0]} ') sys.exit(2) # command line usage error with open(filename, 'rt', encoding='utf-8') as text_file: s = Sentence(text_file.read()) @@ -59,7 +59,7 @@ def main(): print(word) break else: - warnings.warn('last word is #%d, "%s"' % (n, word)) + warnings.warn(f'last word is #{n}, {word!r}') if __name__ == '__main__': main() diff --git a/17-it-generator/sentence_iter2.py b/17-it-generator/sentence_iter2.py index 2663f3f..14ad374 100644 --- a/17-it-generator/sentence_iter2.py +++ b/17-it-generator/sentence_iter2.py @@ -17,14 +17,14 @@ def __init__(self, text): self.text = text def __repr__(self): - return 'Sentence(%s)' % reprlib.repr(self.text) + return f'Sentence({reprlib.repr(self.text)})' def __iter__(self): word_iter = RE_WORD.finditer(self.text) # <1> return SentenceIter(word_iter) # <2> -class SentenceIter(): +class SentenceIter: def __init__(self, word_iter): self.word_iter = word_iter # <3> diff --git a/17-it-generator/tree/extra/pretty_tree.py b/17-it-generator/tree/extra/pretty_tree.py index 668df54..6d6d91a 100644 --- a/17-it-generator/tree/extra/pretty_tree.py +++ b/17-it-generator/tree/extra/pretty_tree.py @@ -1,11 +1,11 @@ from tree import tree SPACES = ' ' * 4 -HLINE = '\u2500' # ─ BOX DRAWINGS LIGHT HORIZONTAL +HLINE = '\u2500' # ─ BOX DRAWINGS LIGHT HORIZONTAL HLINE2 = HLINE * 2 -ELBOW = f'\u2514{HLINE2} ' # └ BOX DRAWINGS LIGHT UP AND RIGHT -TEE = f'\u251C{HLINE2} ' # ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT -PIPE = f'\u2502 ' # │ BOX DRAWINGS LIGHT VERTICAL +ELBOW = f'\u2514{HLINE2} ' # └ BOX DRAWINGS LIGHT UP AND RIGHT +TEE = f'\u251C{HLINE2} ' # ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT +PIPE = '\u2502 ' # │ BOX DRAWINGS LIGHT VERTICAL def render_lines(tree_iter): diff --git a/17-it-generator/tree/extra/test_pretty_tree.py b/17-it-generator/tree/extra/test_pretty_tree.py index 943f0f1..70019a3 100644 --- a/17-it-generator/tree/extra/test_pretty_tree.py +++ b/17-it-generator/tree/extra/test_pretty_tree.py @@ -1,5 +1,3 @@ -import pytest - from pretty_tree import tree, render_lines def test_1_level(): @@ -7,7 +5,7 @@ def test_1_level(): expected = [ 'BrokenPipeError', ] - assert expected == result + assert expected == result def test_2_levels_1_leaf(): @@ -16,7 +14,7 @@ def test_2_levels_1_leaf(): 'IndentationError', '└── TabError', ] - assert expected == result + assert expected == result def test_3_levels_1_leaf(): @@ -29,7 +27,7 @@ class Z(Y): pass '└── Y', ' └── Z', ] - assert expected == result + assert expected == result def test_4_levels_1_leaf(): @@ -98,4 +96,5 @@ class B2(A): pass ] result = list(render_lines(tree(A))) - assert expected == result + assert expected == result + diff --git a/17-it-generator/tree/extra/test_tree.py b/17-it-generator/tree/extra/test_tree.py index da878bc..4c6404c 100644 --- a/17-it-generator/tree/extra/test_tree.py +++ b/17-it-generator/tree/extra/test_tree.py @@ -5,7 +5,7 @@ def test_1_level(): class One: pass expected = [('One', 0, True)] result = list(tree(One)) - assert expected == result + assert expected == result def test_2_levels_2_leaves(): @@ -18,7 +18,7 @@ class Leaf2(Branch): pass ('Leaf2', 1, True), ] result = list(tree(Branch)) - assert expected == result + assert expected == result def test_3_levels_1_leaf(): @@ -31,7 +31,7 @@ class Z(Y): pass ('Z', 2, True), ] result = list(tree(X)) - assert expected == result + assert expected == result def test_4_levels_1_leaf(): @@ -47,7 +47,7 @@ class Level3(Level2): pass ] result = list(tree(Level0)) - assert expected == result + assert expected == result def test_4_levels_3_leaves(): @@ -69,7 +69,7 @@ class D2(C1): pass ] result = list(tree(A)) - assert expected == result + assert expected == result def test_many_levels_1_leaf(): @@ -87,4 +87,4 @@ class Root: pass assert len(result) == level_count assert result[0] == ('Root', 0, True) assert result[-1] == ('Sub99', 99, True) - assert expected == result + assert expected == result diff --git a/17-it-generator/tree/extra/tree.py b/17-it-generator/tree/extra/tree.py index 1a6ec6d..7169a28 100644 --- a/17-it-generator/tree/extra/tree.py +++ b/17-it-generator/tree/extra/tree.py @@ -2,7 +2,7 @@ def tree(cls, level=0, last_in_level=True): yield cls.__name__, level, last_in_level subclasses = cls.__subclasses__() if subclasses: - last = subclasses[-1] + last = subclasses[-1] for sub_cls in subclasses: yield from tree(sub_cls, level+1, sub_cls is last) diff --git a/17-it-generator/tree/step0/test_tree.py b/17-it-generator/tree/step0/test_tree.py index 1ab22ae..8684dbb 100644 --- a/17-it-generator/tree/step0/test_tree.py +++ b/17-it-generator/tree/step0/test_tree.py @@ -5,4 +5,4 @@ def test_1_level(): class One: pass expected = ['One'] result = list(tree(One)) - assert expected == result + assert expected == result diff --git a/17-it-generator/tree/step0/tree.py b/17-it-generator/tree/step0/tree.py index 5466949..07e34af 100644 --- a/17-it-generator/tree/step0/tree.py +++ b/17-it-generator/tree/step0/tree.py @@ -8,4 +8,4 @@ def display(cls): if __name__ == '__main__': - display(BaseException) \ No newline at end of file + display(BaseException) diff --git a/17-it-generator/tree/step1/test_tree.py b/17-it-generator/tree/step1/test_tree.py index 9896bb3..c89a363 100644 --- a/17-it-generator/tree/step1/test_tree.py +++ b/17-it-generator/tree/step1/test_tree.py @@ -5,7 +5,7 @@ def test_1_level(): class One: pass expected = [('One', 0)] result = list(tree(One)) - assert expected == result + assert expected == result def test_2_levels_2_leaves(): @@ -18,4 +18,4 @@ class Leaf2(Branch): pass ('Leaf2', 1), ] result = list(tree(Branch)) - assert expected == result + assert expected == result diff --git a/17-it-generator/tree/step2/test_tree.py b/17-it-generator/tree/step2/test_tree.py index 9896bb3..c89a363 100644 --- a/17-it-generator/tree/step2/test_tree.py +++ b/17-it-generator/tree/step2/test_tree.py @@ -5,7 +5,7 @@ def test_1_level(): class One: pass expected = [('One', 0)] result = list(tree(One)) - assert expected == result + assert expected == result def test_2_levels_2_leaves(): @@ -18,4 +18,4 @@ class Leaf2(Branch): pass ('Leaf2', 1), ] result = list(tree(Branch)) - assert expected == result + assert expected == result diff --git a/17-it-generator/tree/step3/test_tree.py b/17-it-generator/tree/step3/test_tree.py index 37a685c..673ed14 100644 --- a/17-it-generator/tree/step3/test_tree.py +++ b/17-it-generator/tree/step3/test_tree.py @@ -5,7 +5,7 @@ def test_1_level(): class One: pass expected = [('One', 0)] result = list(tree(One)) - assert expected == result + assert expected == result def test_2_levels_2_leaves(): @@ -18,7 +18,7 @@ class Leaf2(Branch): pass ('Leaf2', 1), ] result = list(tree(Branch)) - assert expected == result + assert expected == result def test_3_levels_1_leaf(): @@ -31,4 +31,4 @@ class Z(Y): pass ('Z', 2), ] result = list(tree(X)) - assert expected == result + assert expected == result diff --git a/17-it-generator/tree/step4/test_tree.py b/17-it-generator/tree/step4/test_tree.py index 602941f..44c79ce 100644 --- a/17-it-generator/tree/step4/test_tree.py +++ b/17-it-generator/tree/step4/test_tree.py @@ -5,7 +5,7 @@ def test_1_level(): class One: pass expected = [('One', 0)] result = list(tree(One)) - assert expected == result + assert expected == result def test_2_levels_2_leaves(): @@ -18,7 +18,7 @@ class Leaf2(Branch): pass ('Leaf2', 1), ] result = list(tree(Branch)) - assert expected == result + assert expected == result def test_3_levels_1_leaf(): @@ -31,7 +31,7 @@ class Z(Y): pass ('Z', 2), ] result = list(tree(X)) - assert expected == result + assert expected == result def test_4_levels_1_leaf(): @@ -47,7 +47,7 @@ class Level3(Level2): pass ] result = list(tree(Level0)) - assert expected == result + assert expected == result def test_4_levels_3_leaves(): @@ -69,4 +69,5 @@ class C2(B2): pass ] result = list(tree(A)) - assert expected == result + assert expected == result + diff --git a/17-it-generator/tree/step5/test_tree.py b/17-it-generator/tree/step5/test_tree.py index d1621b5..fed7a16 100644 --- a/17-it-generator/tree/step5/test_tree.py +++ b/17-it-generator/tree/step5/test_tree.py @@ -5,7 +5,7 @@ def test_1_level(): class One: pass expected = [('One', 0)] result = list(tree(One)) - assert expected == result + assert expected == result def test_2_levels_2_leaves(): @@ -18,7 +18,7 @@ class Leaf2(Branch): pass ('Leaf2', 1), ] result = list(tree(Branch)) - assert expected == result + assert expected == result def test_3_levels_1_leaf(): @@ -31,7 +31,7 @@ class Z(Y): pass ('Z', 2), ] result = list(tree(X)) - assert expected == result + assert expected == result def test_4_levels_1_leaf(): @@ -47,7 +47,7 @@ class Level3(Level2): pass ] result = list(tree(Level0)) - assert expected == result + assert expected == result def test_4_levels_3_leaves(): @@ -69,7 +69,7 @@ class D2(C1): pass ] result = list(tree(A)) - assert expected == result + assert expected == result def test_many_levels_1_leaf(): @@ -87,4 +87,5 @@ class Root: pass assert len(result) == level_count assert result[0] == ('Root', 0) assert result[-1] == ('Sub99', 99) - assert expected == result + assert expected == result + diff --git a/17-it-generator/tree/step6/test_tree.py b/17-it-generator/tree/step6/test_tree.py index d1621b5..fed7a16 100644 --- a/17-it-generator/tree/step6/test_tree.py +++ b/17-it-generator/tree/step6/test_tree.py @@ -5,7 +5,7 @@ def test_1_level(): class One: pass expected = [('One', 0)] result = list(tree(One)) - assert expected == result + assert expected == result def test_2_levels_2_leaves(): @@ -18,7 +18,7 @@ class Leaf2(Branch): pass ('Leaf2', 1), ] result = list(tree(Branch)) - assert expected == result + assert expected == result def test_3_levels_1_leaf(): @@ -31,7 +31,7 @@ class Z(Y): pass ('Z', 2), ] result = list(tree(X)) - assert expected == result + assert expected == result def test_4_levels_1_leaf(): @@ -47,7 +47,7 @@ class Level3(Level2): pass ] result = list(tree(Level0)) - assert expected == result + assert expected == result def test_4_levels_3_leaves(): @@ -69,7 +69,7 @@ class D2(C1): pass ] result = list(tree(A)) - assert expected == result + assert expected == result def test_many_levels_1_leaf(): @@ -87,4 +87,5 @@ class Root: pass assert len(result) == level_count assert result[0] == ('Root', 0) assert result[-1] == ('Sub99', 99) - assert expected == result + assert expected == result + diff --git a/19-coroutine/coroutil.py b/19-coroutine/coroutil.py index ecd5de7..4420db8 100644 --- a/19-coroutine/coroutil.py +++ b/19-coroutine/coroutil.py @@ -4,8 +4,8 @@ def coroutine(func): """Decorator: primes `func` by advancing to first `yield`""" @wraps(func) - def primer(*args,**kwargs): # <1> - gen = func(*args,**kwargs) # <2> + def primer(*args, **kwargs): # <1> + gen = func(*args, **kwargs) # <2> next(gen) # <3> return gen # <4> return primer diff --git a/19-coroutine/taxi_sim.py b/19-coroutine/taxi_sim.py index 97933bb..6bf0ce9 100644 --- a/19-coroutine/taxi_sim.py +++ b/19-coroutine/taxi_sim.py @@ -49,11 +49,11 @@ """ -import random +import argparse import collections +import random import queue -import argparse -import time + DEFAULT_NUMBER_OF_TAXIS = 3 DEFAULT_END_TIME = 180 @@ -126,8 +126,8 @@ def compute_duration(previous_action): elif previous_action == 'going home': interval = 1 else: - raise ValueError('Unknown previous_action: %s' % previous_action) - return int(random.expovariate(1/interval)) + 1 + raise ValueError(f'Unknown previous_action: {previous_action}') + return int(random.expovariate(1 / interval)) + 1 def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS, @@ -136,7 +136,7 @@ def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS, if seed is not None: random.seed(seed) # get reproducible results - taxis = {i: taxi_process(i, (i+1)*2, i*DEPARTURE_INTERVAL) + taxis = {i: taxi_process(i, (i + 1) * 2, i * DEPARTURE_INTERVAL) for i in range(num_taxis)} sim = Simulator(taxis) sim.run(end_time) diff --git a/19-coroutine/taxi_sim0.py b/19-coroutine/taxi_sim0.py index 4078fdd..ed988f5 100644 --- a/19-coroutine/taxi_sim0.py +++ b/19-coroutine/taxi_sim0.py @@ -27,11 +27,10 @@ """ -import sys -import random +import argparse import collections import queue -import argparse +import random DEFAULT_NUMBER_OF_TAXIS = 3 DEFAULT_END_TIME = 80 @@ -44,7 +43,7 @@ def compute_delay(interval): """Compute action delay using exponential distribution""" - return int(random.expovariate(1/interval)) + 1 + return int(random.expovariate(1 / interval)) + 1 # BEGIN TAXI_PROCESS def taxi_process(ident, trips, start_time=0): # <1> @@ -68,7 +67,6 @@ def __init__(self, procs_map): self.events = queue.PriorityQueue() self.procs = dict(procs_map) - def run(self, end_time): # <1> """Schedule and display events until time is up""" # schedule the first event for each cab @@ -108,7 +106,7 @@ def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS, if seed is not None: random.seed(seed) # get reproducible results - taxis = {i: taxi_process(i, (i+1)*2, i*DEPARTURE_INTERVAL) + taxis = {i: taxi_process(i, (i + 1) * 2, i * DEPARTURE_INTERVAL) for i in range(num_taxis)} sim = Simulator(taxis) sim.run(end_time) diff --git a/21-futures/getflags/requirements.txt b/21-futures/getflags/requirements.txt index 447f2e9..0971a38 100644 --- a/21-futures/getflags/requirements.txt +++ b/21-futures/getflags/requirements.txt @@ -5,7 +5,7 @@ certifi==2020.12.5 chardet==4.0.0 idna==2.10 requests==2.25.1 -urllib3==1.26.4 +urllib3==1.26.3 tqdm==4.56.2 multidict==5.1.0 yarl==1.6.3 diff --git a/22-async/domains/curio/domainlib.py b/22-async/domains/curio/domainlib.py index d359a21..5469fbf 100644 --- a/22-async/domains/curio/domainlib.py +++ b/22-async/domains/curio/domainlib.py @@ -1,15 +1,16 @@ -from curio import TaskGroup -import curio.socket as socket from collections.abc import Iterable, AsyncIterator from typing import NamedTuple +from curio import TaskGroup +import curio.socket as socket + class Result(NamedTuple): domain: str found: bool - async def probe(domain: str) -> Result: +async def probe(domain: str) -> Result: try: await socket.getaddrinfo(domain, None) except socket.gaierror: diff --git a/23-dyn-attr-prop/oscon/schedule_v2.py b/23-dyn-attr-prop/oscon/schedule_v2.py index 14f1ed4..35827eb 100644 --- a/23-dyn-attr-prop/oscon/schedule_v2.py +++ b/23-dyn-attr-prop/oscon/schedule_v2.py @@ -16,8 +16,8 @@ """ # tag::SCHEDULE2_RECORD[] -import json import inspect # <1> +import json JSON_PATH = 'data/osconfeed.json' diff --git a/23-dyn-attr-prop/oscon/schedule_v3.py b/23-dyn-attr-prop/oscon/schedule_v3.py index c443a39..786a412 100644 --- a/23-dyn-attr-prop/oscon/schedule_v3.py +++ b/23-dyn-attr-prop/oscon/schedule_v3.py @@ -20,8 +20,8 @@ # end::SCHEDULE3_DEMO[] """ -import json import inspect +import json JSON_PATH = 'data/osconfeed.json' diff --git a/23-dyn-attr-prop/oscon/schedule_v4_hasattr.py b/23-dyn-attr-prop/oscon/schedule_v4_hasattr.py index 93a12d1..ac5d5de 100644 --- a/23-dyn-attr-prop/oscon/schedule_v4_hasattr.py +++ b/23-dyn-attr-prop/oscon/schedule_v4_hasattr.py @@ -18,8 +18,8 @@ # end::SCHEDULE4_DEMO[] """ -import json import inspect +import json JSON_PATH = 'data/osconfeed.json' diff --git a/24-descriptor/bulkfood/bulkfood_v4.py b/24-descriptor/bulkfood/bulkfood_v4.py index b672e89..bb05da9 100644 --- a/24-descriptor/bulkfood/bulkfood_v4.py +++ b/24-descriptor/bulkfood/bulkfood_v4.py @@ -54,7 +54,7 @@ def __set__(self, instance, value): # <3> msg = f'{self.storage_name} must be > 0' raise ValueError(msg) - # no __get__ needed + # no __get__ needed # <4> class LineItem: weight = Quantity() # <5> diff --git a/24-descriptor/bulkfood/model_v5.py b/24-descriptor/bulkfood/model_v5.py index 647de18..018afcd 100644 --- a/24-descriptor/bulkfood/model_v5.py +++ b/24-descriptor/bulkfood/model_v5.py @@ -32,5 +32,5 @@ def validate(self, name, value): value = value.strip() if len(value) == 0: raise ValueError(f'{name} cannot be blank') - return value # <8> + return value # <2> # end::MODEL_V5_VALIDATED_SUB[] diff --git a/24-descriptor/descriptorkinds.py b/24-descriptor/descriptorkinds.py index 0e2710c..44f4018 100644 --- a/24-descriptor/descriptorkinds.py +++ b/24-descriptor/descriptorkinds.py @@ -5,20 +5,20 @@ >>> obj = Managed() # <1> >>> obj.over # <2> - -> Overriding.__get__(, , + -> Overriding.__get__(, , ) >>> Managed.over # <3> -> Overriding.__get__(, None, ) >>> obj.over = 7 # <4> -> Overriding.__set__(, , 7) >>> obj.over # <5> - -> Overriding.__get__(, , + -> Overriding.__get__(, , ) >>> obj.__dict__['over'] = 8 # <6> >>> vars(obj) # <7> {'over': 8} >>> obj.over # <8> - -> Overriding.__get__(, , + -> Overriding.__get__(, , ) # end::DESCR_KINDS_DEMO1[] @@ -50,7 +50,7 @@ >>> obj = Managed() >>> obj.non_over # <1> - -> NonOverriding.__get__(, , + -> NonOverriding.__get__(, , ) >>> obj.non_over = 7 # <2> >>> obj.non_over # <3> @@ -59,7 +59,7 @@ -> NonOverriding.__get__(, None, ) >>> del obj.non_over # <5> >>> obj.non_over # <6> - -> NonOverriding.__get__(, , + -> NonOverriding.__get__(, , ) # end::DESCR_KINDS_DEMO3[] diff --git a/25-class-metaprog/autoconst/autoconst.py b/25-class-metaprog/autoconst/autoconst.py new file mode 100644 index 0000000..ac34342 --- /dev/null +++ b/25-class-metaprog/autoconst/autoconst.py @@ -0,0 +1,22 @@ +# tag::WilyDict[] +class WilyDict(dict): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.__next_value = 0 + + def __missing__(self, key): + if key.startswith('__') and key.endswith('__'): + raise KeyError(key) + self[key] = value = self.__next_value + self.__next_value += 1 + return value +# end::WilyDict[] + +# tag::AUTOCONST[] +class AutoConstMeta(type): + def __prepare__(name, bases, **kwargs): + return WilyDict() + +class AutoConst(metaclass=AutoConstMeta): + pass +# end::AUTOCONST[] diff --git a/25-class-metaprog/autoconst/autoconst_demo.py b/25-class-metaprog/autoconst/autoconst_demo.py new file mode 100755 index 0000000..008eae8 --- /dev/null +++ b/25-class-metaprog/autoconst/autoconst_demo.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +""" +Testing ``WilyDict``:: + + >>> from autoconst import WilyDict + >>> wd = WilyDict() + >>> len(wd) + 0 + >>> wd['first'] + 0 + >>> wd + {'first': 0} + >>> wd['second'] + 1 + >>> wd['third'] + 2 + >>> len(wd) + 3 + >>> wd + {'first': 0, 'second': 1, 'third': 2} + >>> wd['__magic__'] + Traceback (most recent call last): + ... + KeyError: '__magic__' + +Testing ``AutoConst``:: + + >>> from autoconst import AutoConst + +# tag::AUTOCONST[] + >>> class Flavor(AutoConst): + ... banana + ... coconut + ... vanilla + ... + >>> Flavor.vanilla + 2 + >>> Flavor.banana, Flavor.coconut + (0, 1) + +# end::AUTOCONST[] + +""" + +from autoconst import AutoConst + + +class Flavor(AutoConst): + banana + coconut + vanilla + + +print('Flavor.vanilla ==', Flavor.vanilla) \ No newline at end of file diff --git a/25-class-metaprog/bulkfood/README.md b/25-class-metaprog/bulkfood/README.md new file mode 100644 index 0000000..61104c8 --- /dev/null +++ b/25-class-metaprog/bulkfood/README.md @@ -0,0 +1,34 @@ +# Legacy Class Descriptor and Metaclass Examples + +Examples from _Fluent Python, First Edition_—Chapter 21, _Class Metaprogramming_, +that are mentioned in _Fluent Python, Second Edition_—Chapter 25, _Class Metaprogramming_. + +These examples were developed with Python 3.4. +They run correctly in Python 3.9, but now it is easier to fullfill the same requirements +without resorting to class decorators or metaclasses. + +I have preserved them here as examples of class metaprogramming techniques +that you may find in legacy code, and that can be refactored to simpler code +using a base class with `__init_subclass__` and decorators implementing `__set_name__`. + +## Suggested Exercise + +If you'd like to practice the concepts presented in chapters 24 and 25 of +_Fluent Python, Second Edition_, +you may to refactor the most advanced example, `model_v8.py` with these changes: + +1. Simplify the `AutoStorage` descriptor by implementing `__set_name__`. +This will allow you to simplify the `EntityMeta` metaclass as well. + +2. Rewrite the `Entity` class to use `__init_subclass__` instead of the `EntityMeta` metaclass—which you can then delete. + +Nothing should change in the `bulkfood_v8.py` code, and its doctests should still pass. + +To run the doctests while refactoring, it's often convenient to pass the `-f` option, +to exit the test runner on the first failing test. + +``` +$ python3 -m doctest -f bulkfood_v8.py +``` + +Enjoy! diff --git a/25-class-metaprog/bulkfood/bulkfood_v6.py b/25-class-metaprog/bulkfood/bulkfood_v6.py new file mode 100644 index 0000000..18b2f40 --- /dev/null +++ b/25-class-metaprog/bulkfood/bulkfood_v6.py @@ -0,0 +1,84 @@ +""" + +A line item for a bulk food order has description, weight and price fields:: + + >>> raisins = LineItem('Golden raisins', 10, 6.95) + >>> raisins.weight, raisins.description, raisins.price + (10, 'Golden raisins', 6.95) + +A ``subtotal`` method gives the total price for that line item:: + + >>> raisins.subtotal() + 69.5 + +The weight of a ``LineItem`` must be greater than 0:: + + >>> raisins.weight = -20 + Traceback (most recent call last): + ... + ValueError: value must be > 0 + +No change was made:: + + >>> raisins.weight + 10 + +The value of the attributes managed by the descriptors are stored in +alternate attributes, created by the descriptors in each ``LineItem`` +instance:: + +# tag::LINEITEM_V6_DEMO[] + >>> raisins = LineItem('Golden raisins', 10, 6.95) + >>> dir(raisins)[:3] + ['_NonBlank#description', '_Quantity#price', '_Quantity#weight'] + >>> LineItem.description.storage_name + '_NonBlank#description' + >>> raisins.description + 'Golden raisins' + >>> getattr(raisins, '_NonBlank#description') + 'Golden raisins' + +# end::LINEITEM_V6_DEMO[] + +If the descriptor is accessed in the class, the descriptor object is +returned: + + >>> LineItem.weight # doctest: +ELLIPSIS + + >>> LineItem.weight.storage_name + '_Quantity#weight' + + +The `NonBlank` descriptor prevents empty or blank strings to be used +for the description: + + >>> br_nuts = LineItem('Brazil Nuts', 10, 34.95) + >>> br_nuts.description = ' ' + Traceback (most recent call last): + ... + ValueError: value cannot be empty or blank + >>> void = LineItem('', 1, 1) + Traceback (most recent call last): + ... + ValueError: value cannot be empty or blank + + +""" + +# tag::LINEITEM_V6[] +import model_v6 as model + +@model.entity # <1> +class LineItem: + description = model.NonBlank() + weight = model.Quantity() + price = model.Quantity() + + def __init__(self, description, weight, price): + self.description = description + self.weight = weight + self.price = price + + def subtotal(self): + return self.weight * self.price +# end::LINEITEM_V6[] diff --git a/25-class-metaprog/bulkfood/bulkfood_v7.py b/25-class-metaprog/bulkfood/bulkfood_v7.py new file mode 100644 index 0000000..cbb5d82 --- /dev/null +++ b/25-class-metaprog/bulkfood/bulkfood_v7.py @@ -0,0 +1,79 @@ +""" + +A line item for a bulk food order has description, weight and price fields:: + + >>> raisins = LineItem('Golden raisins', 10, 6.95) + >>> raisins.weight, raisins.description, raisins.price + (10, 'Golden raisins', 6.95) + +A ``subtotal`` method gives the total price for that line item:: + + >>> raisins.subtotal() + 69.5 + +The weight of a ``LineItem`` must be greater than 0:: + + >>> raisins.weight = -20 + Traceback (most recent call last): + ... + ValueError: value must be > 0 + +No change was made:: + + >>> raisins.weight + 10 + +The value of the attributes managed by the descriptors are stored in +alternate attributes, created by the descriptors in each ``LineItem`` +instance:: + + >>> raisins = LineItem('Golden raisins', 10, 6.95) + >>> dir(raisins)[:3] + ['_NonBlank#description', '_Quantity#price', '_Quantity#weight'] + >>> LineItem.description.storage_name + '_NonBlank#description' + >>> raisins.description + 'Golden raisins' + >>> getattr(raisins, '_NonBlank#description') + 'Golden raisins' + +If the descriptor is accessed in the class, the descriptor object is +returned: + + >>> LineItem.weight # doctest: +ELLIPSIS + + >>> LineItem.weight.storage_name + '_Quantity#weight' + + +The `NonBlank` descriptor prevents empty or blank strings to be used +for the description: + + >>> br_nuts = LineItem('Brazil Nuts', 10, 34.95) + >>> br_nuts.description = ' ' + Traceback (most recent call last): + ... + ValueError: value cannot be empty or blank + >>> void = LineItem('', 1, 1) + Traceback (most recent call last): + ... + ValueError: value cannot be empty or blank + +""" + +# tag::LINEITEM_V7[] +import model_v7 as model + +class LineItem(model.Entity): # <1> + description = model.NonBlank() + weight = model.Quantity() + price = model.Quantity() + + def __init__(self, description, weight, price): + self.description = description + self.weight = weight + self.price = price + + def subtotal(self): + return self.weight * self.price +# end::LINEITEM_V7[] diff --git a/25-class-metaprog/bulkfood/bulkfood_v8.py b/25-class-metaprog/bulkfood/bulkfood_v8.py new file mode 100644 index 0000000..f6ed4d6 --- /dev/null +++ b/25-class-metaprog/bulkfood/bulkfood_v8.py @@ -0,0 +1,86 @@ +""" + +A line item for a bulk food order has description, weight and price fields:: + + >>> raisins = LineItem('Golden raisins', 10, 6.95) + >>> raisins.weight, raisins.description, raisins.price + (10, 'Golden raisins', 6.95) + +A ``subtotal`` method gives the total price for that line item:: + + >>> raisins.subtotal() + 69.5 + +The weight of a ``LineItem`` must be greater than 0:: + + >>> raisins.weight = -20 + Traceback (most recent call last): + ... + ValueError: value must be > 0 + +No change was made:: + + >>> raisins.weight + 10 + + >>> raisins = LineItem('Golden raisins', 10, 6.95) + >>> dir(raisins)[:3] + ['_NonBlank#description', '_Quantity#price', '_Quantity#weight'] + >>> LineItem.description.storage_name + '_NonBlank#description' + >>> raisins.description + 'Golden raisins' + >>> getattr(raisins, '_NonBlank#description') + 'Golden raisins' + +If the descriptor is accessed in the class, the descriptor object is +returned: + + >>> LineItem.weight # doctest: +ELLIPSIS + + >>> LineItem.weight.storage_name + '_Quantity#weight' + + +The `NonBlank` descriptor prevents empty or blank strings to be used +for the description: + + >>> br_nuts = LineItem('Brazil Nuts', 10, 34.95) + >>> br_nuts.description = ' ' + Traceback (most recent call last): + ... + ValueError: value cannot be empty or blank + >>> void = LineItem('', 1, 1) + Traceback (most recent call last): + ... + ValueError: value cannot be empty or blank + + +Fields can be retrieved in the order they were declared: + +# tag::LINEITEM_V8_DEMO[] + >>> for name in LineItem.field_names(): + ... print(name) + ... + description + weight + price + +# end::LINEITEM_V8_DEMO[] + +""" + +import model_v8 as model + +class LineItem(model.Entity): + description = model.NonBlank() + weight = model.Quantity() + price = model.Quantity() + + def __init__(self, description, weight, price): + self.description = description + self.weight = weight + self.price = price + + def subtotal(self): + return self.weight * self.price diff --git a/25-class-metaprog/bulkfood/model_v6.py b/25-class-metaprog/bulkfood/model_v6.py new file mode 100644 index 0000000..716cf89 --- /dev/null +++ b/25-class-metaprog/bulkfood/model_v6.py @@ -0,0 +1,60 @@ +import abc + + +class AutoStorage: + __counter = 0 + + def __init__(self): + cls = self.__class__ + prefix = cls.__name__ + index = cls.__counter + self.storage_name = '_{}#{}'.format(prefix, index) + cls.__counter += 1 + + def __get__(self, instance, owner): + if instance is None: + return self + else: + return getattr(instance, self.storage_name) + + def __set__(self, instance, value): + setattr(instance, self.storage_name, value) + + +class Validated(abc.ABC, AutoStorage): + + def __set__(self, instance, value): + value = self.validate(instance, value) + super().__set__(instance, value) + + @abc.abstractmethod + def validate(self, instance, value): + """return validated value or raise ValueError""" + + +class Quantity(Validated): + """a number greater than zero""" + + def validate(self, instance, value): + if value <= 0: + raise ValueError('value must be > 0') + return value + + +class NonBlank(Validated): + """a string with at least one non-space character""" + + def validate(self, instance, value): + value = value.strip() + if len(value) == 0: + raise ValueError('value cannot be empty or blank') + return value + +# tag::MODEL_V6[] +def entity(cls): # <1> + for key, attr in cls.__dict__.items(): # <2> + if isinstance(attr, Validated): # <3> + type_name = type(attr).__name__ + attr.storage_name = '_{}#{}'.format(type_name, key) # <4> + return cls # <5> +# end::MODEL_V6[] diff --git a/25-class-metaprog/bulkfood/model_v7.py b/25-class-metaprog/bulkfood/model_v7.py new file mode 100644 index 0000000..9e8e34d --- /dev/null +++ b/25-class-metaprog/bulkfood/model_v7.py @@ -0,0 +1,66 @@ +import abc + + +class AutoStorage: + __counter = 0 + + def __init__(self): + cls = self.__class__ + prefix = cls.__name__ + index = cls.__counter + self.storage_name = '_{}#{}'.format(prefix, index) + cls.__counter += 1 + + def __get__(self, instance, owner): + if instance is None: + return self + else: + return getattr(instance, self.storage_name) + + def __set__(self, instance, value): + setattr(instance, self.storage_name, value) + + +class Validated(abc.ABC, AutoStorage): + + def __set__(self, instance, value): + value = self.validate(instance, value) + super().__set__(instance, value) + + @abc.abstractmethod + def validate(self, instance, value): + """return validated value or raise ValueError""" + + +class Quantity(Validated): + """a number greater than zero""" + + def validate(self, instance, value): + if value <= 0: + raise ValueError('value must be > 0') + return value + + +class NonBlank(Validated): + """a string with at least one non-space character""" + + def validate(self, instance, value): + value = value.strip() + if len(value) == 0: + raise ValueError('value cannot be empty or blank') + return value + +# tag::MODEL_V7[] +class EntityMeta(type): + """Metaclass for business entities with validated fields""" + + def __init__(cls, name, bases, attr_dict): + super().__init__(name, bases, attr_dict) # <1> + for key, attr in attr_dict.items(): # <2> + if isinstance(attr, Validated): + type_name = type(attr).__name__ + attr.storage_name = '_{}#{}'.format(type_name, key) + +class Entity(metaclass=EntityMeta): # <3> + """Business entity with validated fields""" +# end::MODEL_V7[] diff --git a/25-class-metaprog/bulkfood/model_v8.py b/25-class-metaprog/bulkfood/model_v8.py new file mode 100644 index 0000000..cc7c332 --- /dev/null +++ b/25-class-metaprog/bulkfood/model_v8.py @@ -0,0 +1,80 @@ +import abc +import collections + + +class AutoStorage: + __counter = 0 + + def __init__(self): + cls = self.__class__ + prefix = cls.__name__ + index = cls.__counter + self.storage_name = '_{}#{}'.format(prefix, index) + cls.__counter += 1 + + def __get__(self, instance, owner): + if instance is None: + return self + else: + return getattr(instance, self.storage_name) + + def __set__(self, instance, value): + setattr(instance, self.storage_name, value) + + +class Validated(abc.ABC, AutoStorage): + + def __set__(self, instance, value): + value = self.validate(instance, value) + super().__set__(instance, value) + + @abc.abstractmethod + def validate(self, instance, value): + """return validated value or raise ValueError""" + + +class Quantity(Validated): + """a number greater than zero""" + + def validate(self, instance, value): + if value <= 0: + raise ValueError('value must be > 0') + return value + + +class NonBlank(Validated): + """a string with at least one non-space character""" + + def validate(self, instance, value): + value = value.strip() + if len(value) == 0: + raise ValueError('value cannot be empty or blank') + return value + +# tag::MODEL_V8[] +class EntityMeta(type): + """Metaclass for business entities with validated fields""" + + @classmethod + def __prepare__(cls, name, bases): + return collections.OrderedDict() # <1> + + def __init__(cls, name, bases, attr_dict): + super().__init__(name, bases, attr_dict) + cls._field_names = [] # <2> + for key, attr in attr_dict.items(): # <3> + if isinstance(attr, Validated): + type_name = type(attr).__name__ + attr.storage_name = '_{}#{}'.format(type_name, key) + cls._field_names.append(key) # <4> + + +class Entity(metaclass=EntityMeta): + """Business entity with validated fields""" + + @classmethod + def field_names(cls): # <5> + for name in cls._field_names: + yield name + +# end::MODEL_V8[] diff --git a/25-class-metaprog/checkeddeco/checkeddeco.py b/25-class-metaprog/checked/decorator/checkeddeco.py similarity index 52% rename from 25-class-metaprog/checkeddeco/checkeddeco.py rename to 25-class-metaprog/checked/decorator/checkeddeco.py index 3eeba15..401835d 100644 --- a/25-class-metaprog/checkeddeco/checkeddeco.py +++ b/25-class-metaprog/checked/decorator/checkeddeco.py @@ -8,13 +8,13 @@ ... class Movie: ... title: str ... year: int - ... megabucks: float + ... box_office: float ... - >>> movie = Movie(title='The Godfather', year=1972, megabucks=137) # <3> + >>> movie = Movie(title='The Godfather', year=1972, box_office=137) >>> movie.title 'The Godfather' - >>> movie # <4> - Movie(title='The Godfather', year=1972, megabucks=137.0) + >>> movie + Movie(title='The Godfather', year=1972, box_office=137.0) # end::MOVIE_DEFINITION[] @@ -23,14 +23,14 @@ # tag::MOVIE_TYPE_VALIDATION[] - >>> movie.year = 'MCMLXXII' # <1> + >>> blockbuster = Movie(title='Avatar', year=2009, box_office='billions') Traceback (most recent call last): ... - TypeError: 'MCMLXXII' is not compatible with year:int - >>> blockbuster = Movie(title='Avatar', year=2009, megabucks='billions') # <2> + TypeError: 'billions' is not compatible with box_office:float + >>> movie.year = 'MCMLXXII' Traceback (most recent call last): ... - TypeError: 'billions' is not compatible with megabucks:float + TypeError: 'MCMLXXII' is not compatible with year:int # end::MOVIE_TYPE_VALIDATION[] @@ -40,13 +40,13 @@ # tag::MOVIE_DEFAULTS[] >>> Movie(title='Life of Brian') - Movie(title='Life of Brian', year=0, megabucks=0.0) + Movie(title='Life of Brian', year=0, box_office=0.0) # end::MOVIE_DEFAULTS[] Providing extra arguments to the constructor is not allowed:: - >>> blockbuster = Movie(title='Avatar', year=2009, megabucks=2000, + >>> blockbuster = Movie(title='Avatar', year=2009, box_office=2000, ... director='James Cameron') Traceback (most recent call last): ... @@ -62,109 +62,90 @@ The `_as_dict` instance creates a `dict` from the attributes of a `Movie` object:: >>> movie._asdict() - {'title': 'The Godfather', 'year': 1972, 'megabucks': 137.0} + {'title': 'The Godfather', 'year': 1972, 'box_office': 137.0} """ from collections.abc import Callable # <1> from typing import Any, NoReturn, get_type_hints -MISSING = object() # <2> - - class Field: - def __init__(self, name: str, constructor: Callable) -> None: # <3> + def __init__(self, name: str, constructor: Callable) -> None: # <2> + if not callable(constructor) or constructor is type(None): + raise TypeError(f'{name!r} type hint must be callable') self.name = name self.constructor = constructor - def __set__(self, instance: Any, value: Any) -> None: # <4> - if value is MISSING: # <5> + def __set__(self, instance: Any, value: Any) -> None: # <3> + if value is ...: # <4> value = self.constructor() else: try: - value = self.constructor(value) # <6> + value = self.constructor(value) # <5> except (TypeError, ValueError) as e: type_name = self.constructor.__name__ msg = ( f'{value!r} is not compatible with {self.name}:{type_name}' ) raise TypeError(msg) from e - instance.__dict__[self.name] = value # <7> - - -# tag::CHECKED_DECORATOR_TOP[] -_methods_to_inject: list[Callable] = [] -_classmethods_to_inject: list[Callable] = [] + instance.__dict__[self.name] = value # <6> -def checked(cls: type) -> type: # <2> - for func in _methods_to_inject: - name = func.__name__ - setattr(cls, name, func) # <5> - for func in _classmethods_to_inject: - name = func.__name__ - setattr(cls, name, classmethod(func)) # <5> +# tag::CHECKED_DECORATOR[] +def checked(cls: type) -> type: # <1> + for name, constructor in _fields(cls).items(): # <2> + setattr(cls, name, Field(name, constructor)) # <3> - for name, constructor in _fields(cls).items(): # <4> - setattr(cls, name, Field(name, constructor)) # <5> - - return cls - - -def _method(func: Callable) -> Callable: - _methods_to_inject.append(func) - return func + cls._fields = classmethod(_fields) #type: ignore # <4> + instance_methods = ( # <5> + __init__, + __repr__, + __setattr__, + _asdict, + __flag_unknown_attrs, + ) + for method in instance_methods: # <6> + setattr(cls, method.__name__, method) -def _classmethod(func: Callable) -> Callable: - _classmethods_to_inject.append(func) - return func + return cls # <7> +# end::CHECKED_DECORATOR[] -# tag::CHECKED_METHODS_TOP[] -@_classmethod -def _fields(cls: type) -> dict[str, type]: # <1> +# tag::CHECKED_METHODS[] +def _fields(cls: type) -> dict[str, type]: return get_type_hints(cls) -@_method def __init__(self: Any, **kwargs: Any) -> None: - for name in self._fields(): # <6> - value = kwargs.pop(name, MISSING) # <7> - setattr(self, name, value) # <8> - if kwargs: # <9> - self.__flag_unknown_attrs(*kwargs) # <10> - -@_method -def __setattr__(self: Any, name: str, value: Any) -> None: # <11> - if name in self._fields(): # <12> + for name in self._fields(): + value = kwargs.pop(name, ...) + setattr(self, name, value) + if kwargs: + self.__flag_unknown_attrs(*kwargs) + +def __setattr__(self: Any, name: str, value: Any) -> None: + if name in self._fields(): cls = self.__class__ descriptor = getattr(cls, name) - descriptor.__set__(self, value) # <13> - else: # <14> + descriptor.__set__(self, value) + else: self.__flag_unknown_attrs(name) -# end::CHECKED_METHODS_TOP[] -# tag::CHECKED_METHODS_BOTTOM[] -@_method -def __flag_unknown_attrs(self: Any, *names: str) -> NoReturn: # <1> +def __flag_unknown_attrs(self: Any, *names: str) -> NoReturn: plural = 's' if len(names) > 1 else '' extra = ', '.join(f'{name!r}' for name in names) cls_name = repr(self.__class__.__name__) raise AttributeError(f'{cls_name} has no attribute{plural} {extra}') - -@_method -def _asdict(self: Any) -> dict[str, Any]: # <2> +def _asdict(self: Any) -> dict[str, Any]: return { name: getattr(self, name) for name, attr in self.__class__.__dict__.items() if isinstance(attr, Field) } - -@_method -def __repr__(self: Any) -> str: # <3> +def __repr__(self: Any) -> str: kwargs = ', '.join( f'{key}={value!r}' for key, value in self._asdict().items() ) return f'{self.__class__.__name__}({kwargs})' -# end::CHECKED_METHODS_BOTTOM[] +# end::CHECKED_METHODS[] diff --git a/25-class-metaprog/checked/decorator/checkeddeco_demo.py b/25-class-metaprog/checked/decorator/checkeddeco_demo.py new file mode 100755 index 0000000..b72acbc --- /dev/null +++ b/25-class-metaprog/checked/decorator/checkeddeco_demo.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +from checkeddeco import checked + +@checked +class Movie: + title: str + year: int + box_office: float + + +if __name__ == '__main__': + # No static type checker can understand this... + movie = Movie(title='The Godfather', year=1972, box_office=137) # type: ignore + print(movie.title) + print(movie) + try: + # remove the "type: ignore" comment to see Mypy correctly spot the error + movie.year = 'MCMLXXII' # type: ignore + except TypeError as e: + print(e) + try: + # Again, no static type checker can understand this... + blockbuster = Movie(title='Avatar', year=2009, box_office='billions') # type: ignore + except TypeError as e: + print(e) diff --git a/25-class-metaprog/checkeddeco/checkeddeco_test.py b/25-class-metaprog/checked/decorator/checkeddeco_test.py similarity index 79% rename from 25-class-metaprog/checkeddeco/checkeddeco_test.py rename to 25-class-metaprog/checked/decorator/checkeddeco_test.py index 7f4d8dc..b79aae3 100644 --- a/25-class-metaprog/checkeddeco/checkeddeco_test.py +++ b/25-class-metaprog/checked/decorator/checkeddeco_test.py @@ -38,3 +38,13 @@ class Cat: felix = Cat(name='Felix', weight=3.2, age=7) assert str(e.value) == "'Cat' has no attribute 'age'" + + +def test_field_invalid_constructor(): + with pytest.raises(TypeError) as e: + @checked + class Cat: + name: str + weight: None + + assert str(e.value) == "'weight' type hint must be callable" \ No newline at end of file diff --git a/25-class-metaprog/checked/checkedlib_demo.py b/25-class-metaprog/checked/initsub/checked_demo.py old mode 100644 new mode 100755 similarity index 65% rename from 25-class-metaprog/checked/checkedlib_demo.py rename to 25-class-metaprog/checked/initsub/checked_demo.py index 203f90b..d21279f --- a/25-class-metaprog/checked/checkedlib_demo.py +++ b/25-class-metaprog/checked/initsub/checked_demo.py @@ -1,13 +1,15 @@ +#!/usr/bin/env python3 + from checkedlib import Checked class Movie(Checked): title: str year: int - megabucks: float + box_office: float if __name__ == '__main__': - movie = Movie(title='The Godfather', year=1972, megabucks=137) + movie = Movie(title='The Godfather', year=1972, box_office=137) print(movie.title) print(movie) try: @@ -16,6 +18,6 @@ class Movie(Checked): except TypeError as e: print(e) try: - blockbuster = Movie(title='Avatar', year=2009, megabucks='billions') + blockbuster = Movie(title='Avatar', year=2009, box_office='billions') except TypeError as e: print(e) diff --git a/25-class-metaprog/checked/checkedlib.py b/25-class-metaprog/checked/initsub/checkedlib.py similarity index 66% rename from 25-class-metaprog/checked/checkedlib.py rename to 25-class-metaprog/checked/initsub/checkedlib.py index 63442e3..01bc0a8 100644 --- a/25-class-metaprog/checked/checkedlib.py +++ b/25-class-metaprog/checked/initsub/checkedlib.py @@ -7,29 +7,29 @@ >>> class Movie(Checked): # <1> ... title: str # <2> ... year: int - ... megabucks: float + ... box_office: float ... - >>> movie = Movie(title='The Godfather', year=1972, megabucks=137) # <3> + >>> movie = Movie(title='The Godfather', year=1972, box_office=137) # <3> >>> movie.title 'The Godfather' >>> movie # <4> - Movie(title='The Godfather', year=1972, megabucks=137.0) + Movie(title='The Godfather', year=1972, box_office=137.0) # end::MOVIE_DEFINITION[] -The type of arguments is runtime checked when an attribute is set, -including during instantiation:: +The type of arguments is runtime checked during instantiation +and when an attribute is set:: # tag::MOVIE_TYPE_VALIDATION[] - >>> movie.year = 'MCMLXXII' # <1> + >>> blockbuster = Movie(title='Avatar', year=2009, box_office='billions') Traceback (most recent call last): ... - TypeError: 'MCMLXXII' is not compatible with year:int - >>> blockbuster = Movie(title='Avatar', year=2009, megabucks='billions') # <2> + TypeError: 'billions' is not compatible with box_office:float + >>> movie.year = 'MCMLXXII' Traceback (most recent call last): ... - TypeError: 'billions' is not compatible with megabucks:float + TypeError: 'MCMLXXII' is not compatible with year:int # end::MOVIE_TYPE_VALIDATION[] @@ -39,29 +39,29 @@ # tag::MOVIE_DEFAULTS[] >>> Movie(title='Life of Brian') - Movie(title='Life of Brian', year=0, megabucks=0.0) + Movie(title='Life of Brian', year=0, box_office=0.0) # end::MOVIE_DEFAULTS[] Providing extra arguments to the constructor is not allowed:: - >>> blockbuster = Movie(title='Avatar', year=2009, megabucks=2000, + >>> blockbuster = Movie(title='Avatar', year=2009, box_office=2000, ... director='James Cameron') Traceback (most recent call last): ... - AttributeError: 'Movie' has no attribute 'director' + AttributeError: 'Movie' object has no attribute 'director' Creating new attributes at runtime is restricted as well:: >>> movie.director = 'Francis Ford Coppola' Traceback (most recent call last): ... - AttributeError: 'Movie' has no attribute 'director' + AttributeError: 'Movie' object has no attribute 'director' The `_as_dict` instance creates a `dict` from the attributes of a `Movie` object:: >>> movie._asdict() - {'title': 'The Godfather', 'year': 1972, 'megabucks': 137.0} + {'title': 'The Godfather', 'year': 1972, 'box_office': 137.0} """ @@ -69,27 +69,25 @@ from collections.abc import Callable # <1> from typing import Any, NoReturn, get_type_hints -MISSING = object() # <2> - class Field: - def __init__(self, name: str, constructor: Callable) -> None: # <3> + def __init__(self, name: str, constructor: Callable) -> None: # <2> + if not callable(constructor) or constructor is type(None): # <3> + raise TypeError(f'{name!r} type hint must be callable') self.name = name self.constructor = constructor - def __set__(self, instance: 'Checked', value: Any) -> None: # <4> - if value is MISSING: # <5> + def __set__(self, instance: Any, value: Any) -> None: + if value is ...: # <4> value = self.constructor() else: try: - value = self.constructor(value) # <6> - except (TypeError, ValueError) as e: + value = self.constructor(value) # <5> + except (TypeError, ValueError) as e: # <6> type_name = self.constructor.__name__ msg = f'{value!r} is not compatible with {self.name}:{type_name}' raise TypeError(msg) from e instance.__dict__[self.name] = value # <7> - - # end::CHECKED_FIELD[] # tag::CHECKED_TOP[] @@ -105,36 +103,36 @@ def __init_subclass__(subclass) -> None: # <2> def __init__(self, **kwargs: Any) -> None: for name in self._fields(): # <6> - value = kwargs.pop(name, MISSING) # <7> + value = kwargs.pop(name, ...) # <7> setattr(self, name, value) # <8> if kwargs: # <9> self.__flag_unknown_attrs(*kwargs) # <10> - def __setattr__(self, name: str, value: Any) -> None: # <11> - if name in self._fields(): # <12> + # end::CHECKED_TOP[] + + # tag::CHECKED_BOTTOM[] + def __setattr__(self, name: str, value: Any) -> None: # <1> + if name in self._fields(): # <2> cls = self.__class__ descriptor = getattr(cls, name) - descriptor.__set__(self, value) # <13> - else: # <14> + descriptor.__set__(self, value) # <3> + else: # <4> self.__flag_unknown_attrs(name) - # end::CHECKED_TOP[] - - # tag::CHECKED_BOTTOM[] - def __flag_unknown_attrs(self, *names: str) -> NoReturn: # <1> + def __flag_unknown_attrs(self, *names: str) -> NoReturn: # <5> plural = 's' if len(names) > 1 else '' extra = ', '.join(f'{name!r}' for name in names) cls_name = repr(self.__class__.__name__) - raise AttributeError(f'{cls_name} has no attribute{plural} {extra}') + raise AttributeError(f'{cls_name} object has no attribute{plural} {extra}') - def _asdict(self) -> dict[str, Any]: # <2> + def _asdict(self) -> dict[str, Any]: # <6> return { name: getattr(self, name) for name, attr in self.__class__.__dict__.items() if isinstance(attr, Field) } - def __repr__(self) -> str: # <3> + def __repr__(self) -> str: # <7> kwargs = ', '.join( f'{key}={value!r}' for key, value in self._asdict().items() ) diff --git a/25-class-metaprog/checked/initsub/checkedlib_test.py b/25-class-metaprog/checked/initsub/checkedlib_test.py new file mode 100644 index 0000000..803b1c9 --- /dev/null +++ b/25-class-metaprog/checked/initsub/checkedlib_test.py @@ -0,0 +1,59 @@ +import pytest + + +from checkedlib import Checked + + +def test_field_validation_type_error(): + class Cat(Checked): + name: str + weight: float + + with pytest.raises(TypeError) as e: + felix = Cat(name='Felix', weight=None) + + assert str(e.value) == 'None is not compatible with weight:float' + + +def test_field_validation_value_error(): + class Cat(Checked): + name: str + weight: float + + with pytest.raises(TypeError) as e: + felix = Cat(name='Felix', weight='half stone') + + assert str(e.value) == "'half stone' is not compatible with weight:float" + + +def test_constructor_attribute_error(): + class Cat(Checked): + name: str + weight: float + + with pytest.raises(AttributeError) as e: + felix = Cat(name='Felix', weight=3.2, age=7) + + assert str(e.value) == "'Cat' object has no attribute 'age'" + + +def test_assignment_attribute_error(): + class Cat(Checked): + name: str + weight: float + + felix = Cat(name='Felix', weight=3.2) + with pytest.raises(AttributeError) as e: + felix.color = 'tan' + + assert str(e.value) == "'Cat' object has no attribute 'color'" + + +def test_field_invalid_constructor(): + with pytest.raises(TypeError) as e: + class Cat(Checked): + name: str + weight: None + + assert str(e.value) == "'weight' type hint must be callable" + diff --git a/25-class-metaprog/checkeddeco/checkeddeco_demo.py b/25-class-metaprog/checked/metaclass/checked_demo.py old mode 100644 new mode 100755 similarity index 52% rename from 25-class-metaprog/checkeddeco/checkeddeco_demo.py rename to 25-class-metaprog/checked/metaclass/checked_demo.py index f4c45e7..27f39b3 --- a/25-class-metaprog/checkeddeco/checkeddeco_demo.py +++ b/25-class-metaprog/checked/metaclass/checked_demo.py @@ -1,22 +1,25 @@ -from checkeddeco import checked +#!/usr/bin/env python3 -@checked -class Movie: +# tag::MOVIE_DEMO[] +from checkedlib import Checked + +class Movie(Checked): title: str year: int - megabucks: float - + box_office: float if __name__ == '__main__': - movie = Movie(title='The Godfather', year=1972, megabucks=137) - print(movie.title) + movie = Movie(title='The Godfather', year=1972, box_office=137) print(movie) + print(movie.title) + # end::MOVIE_DEMO[] + try: # remove the "type: ignore" comment to see Mypy error movie.year = 'MCMLXXII' # type: ignore except TypeError as e: print(e) try: - blockbuster = Movie(title='Avatar', year=2009, megabucks='billions') + blockbuster = Movie(title='Avatar', year=2009, box_office='billions') except TypeError as e: print(e) diff --git a/25-class-metaprog/checked/metaclass/checkedlib.py b/25-class-metaprog/checked/metaclass/checkedlib.py new file mode 100644 index 0000000..34484ac --- /dev/null +++ b/25-class-metaprog/checked/metaclass/checkedlib.py @@ -0,0 +1,148 @@ +""" +A ``Checked`` subclass definition requires that keyword arguments are +used to create an instance, and provides a nice ``__repr__``:: + +# tag::MOVIE_DEFINITION[] + + >>> class Movie(Checked): # <1> + ... title: str # <2> + ... year: int + ... box_office: float + ... + >>> movie = Movie(title='The Godfather', year=1972, box_office=137) # <3> + >>> movie.title + 'The Godfather' + >>> movie # <4> + Movie(title='The Godfather', year=1972, box_office=137.0) + +# end::MOVIE_DEFINITION[] + +The type of arguments is runtime checked during instantiation +and when an attribute is set:: + +# tag::MOVIE_TYPE_VALIDATION[] + + >>> blockbuster = Movie(title='Avatar', year=2009, box_office='billions') + Traceback (most recent call last): + ... + TypeError: 'billions' is not compatible with box_office:float + >>> movie.year = 'MCMLXXII' + Traceback (most recent call last): + ... + TypeError: 'MCMLXXII' is not compatible with year:int + +# end::MOVIE_TYPE_VALIDATION[] + +Attributes not passed as arguments to the constructor are initialized with +default values:: + +# tag::MOVIE_DEFAULTS[] + + >>> Movie(title='Life of Brian') + Movie(title='Life of Brian', year=0, box_office=0.0) + +# end::MOVIE_DEFAULTS[] + +Providing extra arguments to the constructor is not allowed:: + + >>> blockbuster = Movie(title='Avatar', year=2009, box_office=2000, + ... director='James Cameron') + Traceback (most recent call last): + ... + AttributeError: 'Movie' object has no attribute 'director' + +Creating new attributes at runtime is restricted as well:: + + >>> movie.director = 'Francis Ford Coppola' + Traceback (most recent call last): + ... + AttributeError: 'Movie' object has no attribute 'director' + +The `_as_dict` instance creates a `dict` from the attributes of a `Movie` object:: + + >>> movie._asdict() + {'title': 'The Godfather', 'year': 1972, 'box_office': 137.0} + +""" + +from collections.abc import Callable +from typing import Any, NoReturn, get_type_hints + +# tag::CHECKED_FIELD[] +class Field: + def __init__(self, name: str, constructor: Callable) -> None: + if not callable(constructor) or constructor is type(None): + raise TypeError(f'{name!r} type hint must be callable') + self.name = name + self.storage_name = '_' + name # <1> + self.constructor = constructor + + def __get__(self, instance, owner=None): # <2> + return getattr(instance, self.storage_name) # <3> + + def __set__(self, instance: Any, value: Any) -> None: + if value is ...: + value = self.constructor() + else: + try: + value = self.constructor(value) + except (TypeError, ValueError) as e: + type_name = self.constructor.__name__ + msg = f'{value!r} is not compatible with {self.name}:{type_name}' + raise TypeError(msg) from e + setattr(instance, self.storage_name, value) # <4> +# end::CHECKED_FIELD[] + +# tag::CHECKED_META[] +class CheckedMeta(type): + + def __new__(meta_cls, cls_name, bases, cls_dict): # <1> + if '__slots__' not in cls_dict: # <2> + slots = [] + type_hints = cls_dict.get('__annotations__', {}) # <3> + for name, constructor in type_hints.items(): # <4> + field = Field(name, constructor) # <5> + cls_dict[name] = field # <6> + slots.append(field.storage_name) # <7> + + cls_dict['__slots__'] = slots # <8> + + return super().__new__( + meta_cls, cls_name, bases, cls_dict) # <9> +# end::CHECKED_META[] + +# tag::CHECKED_CLASS[] +class Checked(metaclass=CheckedMeta): + __slots__ = () # skip CheckedMeta.__new__ processing + + @classmethod + def _fields(cls) -> dict[str, type]: + return get_type_hints(cls) + + def __init__(self, **kwargs: Any) -> None: + for name in self._fields(): + value = kwargs.pop(name, ...) + setattr(self, name, value) + if kwargs: + self.__flag_unknown_attrs(*kwargs) + + def __flag_unknown_attrs(self, *names: str) -> NoReturn: + plural = 's' if len(names) > 1 else '' + extra = ', '.join(f'{name!r}' for name in names) + cls_name = repr(self.__class__.__name__) + raise AttributeError(f'{cls_name} object has no attribute{plural} {extra}') + + def _asdict(self) -> dict[str, Any]: + return { + name: getattr(self, name) + for name, attr in self.__class__.__dict__.items() + if isinstance(attr, Field) + } + + def __repr__(self) -> str: + kwargs = ', '.join( + f'{key}={value!r}' for key, value in self._asdict().items() + ) + return f'{self.__class__.__name__}({kwargs})' + +# end::CHECKED_CLASS[] diff --git a/25-class-metaprog/checked/checkedlib_test.py b/25-class-metaprog/checked/metaclass/checkedlib_test.py similarity index 58% rename from 25-class-metaprog/checked/checkedlib_test.py rename to 25-class-metaprog/checked/metaclass/checkedlib_test.py index 6635a66..0e3552f 100644 --- a/25-class-metaprog/checked/checkedlib_test.py +++ b/25-class-metaprog/checked/metaclass/checkedlib_test.py @@ -34,4 +34,25 @@ class Cat(Checked): with pytest.raises(AttributeError) as e: felix = Cat(name='Felix', weight=3.2, age=7) - assert str(e.value) == "'Cat' has no attribute 'age'" + assert str(e.value) == "'Cat' object has no attribute 'age'" + + +def test_assignment_attribute_error(): + class Cat(Checked): + name: str + weight: float + + felix = Cat(name='Felix', weight=3.2) + with pytest.raises(AttributeError) as e: + felix.color = 'tan' + + assert str(e.value) == "'Cat' object has no attribute 'color'" + + +def test_field_invalid_constructor(): + with pytest.raises(TypeError) as e: + class Cat(Checked): + name: str + weight: None + + assert str(e.value) == "'weight' type hint must be callable" \ No newline at end of file diff --git a/25-class-metaprog/evalsupport.py b/25-class-metaprog/evalsupport.py deleted file mode 100644 index a7f3b5b..0000000 --- a/25-class-metaprog/evalsupport.py +++ /dev/null @@ -1,29 +0,0 @@ -# tag::BEGINNING[] -print('<[100]> evalsupport module start') - -def deco_alpha(cls): - print('<[200]> deco_alpha') - - def inner_1(self): - print('<[300]> deco_alpha:inner_1') - - cls.method_y = inner_1 - return cls - -# end::BEGINNING[] -# tag::META_ALEPH[] -class MetaAleph(type): - print('<[400]> MetaAleph body') - - def __init__(cls, name, bases, dic): - print('<[500]> MetaAleph.__init__') - - def inner_2(self): - print('<[600]> MetaAleph.__init__:inner_2') - - cls.method_z = inner_2 - -# end::META_ALEPH[] -# tag::END[] -print('<[700]> evalsupport module end') -# end::END[] diff --git a/25-class-metaprog/evaltime.py b/25-class-metaprog/evaltime.py deleted file mode 100644 index 299098a..0000000 --- a/25-class-metaprog/evaltime.py +++ /dev/null @@ -1,49 +0,0 @@ -from evalsupport import deco_alpha - -print('<[1]> evaltime module start') - - -class ClassOne(): - print('<[2]> ClassOne body') - - def __init__(self): - print('<[3]> ClassOne.__init__') - - def __del__(self): - print('<[4]> ClassOne.__del__') - - def method_x(self): - print('<[5]> ClassOne.method_x') - - class ClassTwo(object): - print('<[6]> ClassTwo body') - - -@deco_alpha -class ClassThree(): - print('<[7]> ClassThree body') - - def method_y(self): - print('<[8]> ClassThree.method_y') - - -class ClassFour(ClassThree): - print('<[9]> ClassFour body') - - def method_y(self): - print('<[10]> ClassFour.method_y') - - -if __name__ == '__main__': - print('<[11]> ClassOne tests', 30 * '.') - one = ClassOne() - one.method_x() - print('<[12]> ClassThree tests', 30 * '.') - three = ClassThree() - three.method_y() - print('<[13]> ClassFour tests', 30 * '.') - four = ClassFour() - four.method_y() - - -print('<[14]> evaltime module end') diff --git a/25-class-metaprog/evaltime/builderlib.py b/25-class-metaprog/evaltime/builderlib.py new file mode 100644 index 0000000..5f19ba2 --- /dev/null +++ b/25-class-metaprog/evaltime/builderlib.py @@ -0,0 +1,50 @@ +# tag::BUILDERLIB_TOP[] +print('@ builderlib module start') + +class Builder: # <1> + print('@ Builder body') + + def __init_subclass__(cls): # <2> + print(f'@ Builder.__init_subclass__({cls!r})') + + def inner_0(self): # <3> + print(f'@ SuperA.__init_subclass__:inner_0({self!r})') + + cls.method_a = inner_0 + + def __init__(self): + super().__init__() + print(f'@ Builder.__init__({self!r})') + + +def deco(cls): # <4> + print(f'@ deco({cls!r})') + + def inner_1(self): # <5> + print(f'@ deco:inner_1({self!r})') + + cls.method_b = inner_1 + return cls # <6> +# end::BUILDERLIB_TOP[] + +# tag::BUILDERLIB_BOTTOM[] +class Descriptor: # <1> + print('@ Descriptor body') + + def __init__(self): # <2> + print(f'@ Descriptor.__init__({self!r})') + + def __set_name__(self, owner, name): # <3> + args = (self, owner, name) + print(f'@ Descriptor.__set_name__{args!r}') + + def __set__(self, instance, value): # <4> + args = (self, instance, value) + print(f'@ Descriptor.__set__{args!r}') + + def __repr__(self): + return '' + + +print('@ builderlib module end') +# end::BUILDERLIB_BOTTOM[] diff --git a/25-class-metaprog/evaltime/evaldemo.py b/25-class-metaprog/evaltime/evaldemo.py new file mode 100755 index 0000000..7be9ba1 --- /dev/null +++ b/25-class-metaprog/evaltime/evaldemo.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +from builderlib import Builder, deco, Descriptor + +print('# evaldemo module start') + +@deco # <1> +class Klass(Builder): # <2> + print('# Klass body') + + attr = Descriptor() # <3> + + def __init__(self): + super().__init__() + print(f'# Klass.__init__({self!r})') + + def __repr__(self): + return '' + + +def main(): # <4> + obj = Klass() + obj.method_a() + obj.method_b() + obj.attr = 999 + +if __name__ == '__main__': + main() + +print('# evaldemo module end') diff --git a/25-class-metaprog/evaltime/evaldemo_meta.py b/25-class-metaprog/evaltime/evaldemo_meta.py new file mode 100755 index 0000000..dade50f --- /dev/null +++ b/25-class-metaprog/evaltime/evaldemo_meta.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +from builderlib import Builder, deco, Descriptor +from metalib import MetaKlass # <1> + +print('# evaldemo_meta module start') + +@deco +class Klass(Builder, metaclass=MetaKlass): # <2> + print('# Klass body') + + attr = Descriptor() + + def __init__(self): + super().__init__() + print(f'# Klass.__init__({self!r})') + + def __repr__(self): + return '' + + +def main(): + obj = Klass() + obj.method_a() + obj.method_b() + obj.method_c() # <3> + obj.attr = 999 + + +if __name__ == '__main__': + main() + +print('# evaldemo_meta module end') diff --git a/25-class-metaprog/evaltime/metalib.py b/25-class-metaprog/evaltime/metalib.py new file mode 100644 index 0000000..82f4ebf --- /dev/null +++ b/25-class-metaprog/evaltime/metalib.py @@ -0,0 +1,43 @@ +# tag::METALIB_TOP[] +print('% metalib module start') + +import collections + +class NosyDict(collections.UserDict): + def __setitem__(self, key, value): + args = (self, key, value) + print(f'% NosyDict.__setitem__{args!r}') + super().__setitem__(key, value) + + def __repr__(self): + return '' +# end::METALIB_TOP[] + +# tag::METALIB_BOTTOM[] +class MetaKlass(type): + print('% MetaKlass body') + + @classmethod # <1> + def __prepare__(meta_cls, cls_name, bases): # <2> + args = (meta_cls, cls_name, bases) + print(f'% MetaKlass.__prepare__{args!r}') + return NosyDict() # <3> + + def __new__(meta_cls, cls_name, bases, cls_dict): # <4> + args = (meta_cls, cls_name, bases, cls_dict) + print(f'% MetaKlass.__new__{args!r}') + def inner_2(self): + print(f'% MetaKlass.__new__:inner_2({self!r})') + + cls = super().__new__(meta_cls, cls_name, bases, cls_dict.data) # <5> + + cls.method_c = inner_2 # <6> + + return cls # <7> + + def __repr__(cls): # <8> + cls_name = cls.__name__ + return f"" + +print('% metalib module end') +# end::METALIB_BOTTOM[] diff --git a/25-class-metaprog/evaltime_meta.py b/25-class-metaprog/evaltime_meta.py deleted file mode 100644 index 41ed098..0000000 --- a/25-class-metaprog/evaltime_meta.py +++ /dev/null @@ -1,53 +0,0 @@ -from evalsupport import deco_alpha -from evalsupport import MetaAleph - -print('<[1]> evaltime_meta module start') - - -@deco_alpha -class ClassThree(): - print('<[2]> ClassThree body') - - def method_y(self): - print('<[3]> ClassThree.method_y') - - -class ClassFour(ClassThree): - print('<[4]> ClassFour body') - - def method_y(self): - print('<[5]> ClassFour.method_y') - - -class ClassFive(metaclass=MetaAleph): - print('<[6]> ClassFive body') - - def __init__(self): - print('<[7]> ClassFive.__init__') - - def method_z(self): - print('<[8]> ClassFive.method_z') - - -class ClassSix(ClassFive): - print('<[9]> ClassSix body') - - def method_z(self): - print('<[10]> ClassSix.method_z') - - -if __name__ == '__main__': - print('<[11]> ClassThree tests', 30 * '.') - three = ClassThree() - three.method_y() - print('<[12]> ClassFour tests', 30 * '.') - four = ClassFour() - four.method_y() - print('<[13]> ClassFive tests', 30 * '.') - five = ClassFive() - five.method_z() - print('<[14]> ClassSix tests', 30 * '.') - six = ClassSix() - six.method_z() - -print('<[15]> evaltime_meta module end') diff --git a/25-class-metaprog/factories.py b/25-class-metaprog/factories.py index fb2a9c5..38ec763 100644 --- a/25-class-metaprog/factories.py +++ b/25-class-metaprog/factories.py @@ -27,35 +27,48 @@ """ + # tag::RECORD_FACTORY[] -def record_factory(cls_name, field_names): - try: - field_names = field_names.replace(',', ' ').split() # <1> - except AttributeError: # no .replace or .split - pass # assume it's already a sequence of strings - field_names = tuple(field_names) # <2> - if not all(s.isidentifier() for s in field_names): - raise ValueError('field_names must all be valid identifiers') - - def __init__(self, *args, **kwargs): # <3> +from typing import Union, Any +from collections.abc import Iterable, Iterator + +FieldNames = Union[str, Iterable[str]] # <1> + +def record_factory(cls_name: str, field_names: FieldNames) -> type[tuple]: # <2> + + slots = parse_identifiers(field_names) # <3> + + def __init__(self, *args, **kwargs) -> None: # <4> attrs = dict(zip(self.__slots__, args)) attrs.update(kwargs) for name, value in attrs.items(): setattr(self, name, value) - def __iter__(self): # <4> + def __iter__(self) -> Iterator[Any]: # <5> for name in self.__slots__: yield getattr(self, name) - def __repr__(self): # <5> - values = ', '.join('{}={!r}'.format(*i) for i - in zip(self.__slots__, self)) - return '{}({})'.format(self.__class__.__name__, values) + def __repr__(self): # <6> + values = ', '.join( + '{}={!r}'.format(*i) for i in zip(self.__slots__, self) + ) + cls_name = self.__class__.__name__ + return f'{cls_name}({values})' + + cls_attrs = dict( # <7> + __slots__=slots, + __init__=__init__, + __iter__=__iter__, + __repr__=__repr__, + ) + + return type(cls_name, (object,), cls_attrs) # <8> - cls_attrs = dict(__slots__ = field_names, # <6> - __init__ = __init__, - __iter__ = __iter__, - __repr__ = __repr__) - return type(cls_name, (object,), cls_attrs) # <7> +def parse_identifiers(names: FieldNames) -> tuple[str, ...]: + if isinstance(names, str): + names = names.replace(',', ' ').split() # <9> + if not all(s.isidentifier() for s in names): + raise ValueError('names must all be valid identifiers') + return tuple(names) # end::RECORD_FACTORY[] diff --git a/25-class-metaprog/factories_ducktyped.py b/25-class-metaprog/factories_ducktyped.py new file mode 100644 index 0000000..a7a509a --- /dev/null +++ b/25-class-metaprog/factories_ducktyped.py @@ -0,0 +1,79 @@ +""" +record_factory: create simple classes just for holding data fields + +# tag::RECORD_FACTORY_DEMO[] + >>> Dog = record_factory('Dog', 'name weight owner') # <1> + >>> rex = Dog('Rex', 30, 'Bob') + >>> rex # <2> + Dog(name='Rex', weight=30, owner='Bob') + >>> name, weight, _ = rex # <3> + >>> name, weight + ('Rex', 30) + >>> "{2}'s dog weighs {1}kg".format(*rex) # <4> + "Bob's dog weighs 30kg" + >>> rex.weight = 32 # <5> + >>> rex + Dog(name='Rex', weight=32, owner='Bob') + >>> Dog.__mro__ # <6> + (, ) + +# end::RECORD_FACTORY_DEMO[] + +The factory also accepts a list or tuple of identifiers: + + >>> Dog = record_factory('Dog', ['name', 'weight', 'owner']) + >>> Dog.__slots__ + ('name', 'weight', 'owner') + +""" + + +# tag::RECORD_FACTORY[] +from typing import Union, Any +from collections.abc import Sequence, Iterator + +FieldNames = Union[str, Sequence[str]] + + +def parse_identifiers(names): + try: + names = names.replace(',', ' ').split() # <1> + except AttributeError: # no .replace or .split + pass # assume it's already a sequence of strings + if not all(s.isidentifier() for s in names): + raise ValueError('names must all be valid identifiers') + return tuple(names) + + +def record_factory(cls_name, field_names): + + field_identifiers = parse_identifiers(field_names) + + def __init__(self, *args, **kwargs) -> None: # <4> + attrs = dict(zip(self.__slots__, args)) + attrs.update(kwargs) + for name, value in attrs.items(): + setattr(self, name, value) + + def __iter__(self) -> Iterator[Any]: # <5> + for name in self.__slots__: + yield getattr(self, name) + + def __repr__(self): # <6> + values = ', '.join( + '{}={!r}'.format(*i) for i in zip(self.__slots__, self) + ) + cls_name = self.__class__.__name__ + return f'{cls_name}({values})' + + cls_attrs = dict( + __slots__=field_identifiers, # <7> + __init__=__init__, + __iter__=__iter__, + __repr__=__repr__, + ) + + return type(cls_name, (object,), cls_attrs) # <8> + + +# end::RECORD_FACTORY[] diff --git a/25-class-metaprog/hours/hours.py b/25-class-metaprog/hours/hours.py new file mode 100644 index 0000000..c673144 --- /dev/null +++ b/25-class-metaprog/hours/hours.py @@ -0,0 +1,115 @@ +""" +Abusing ``__class_getitem__`` to make a nano-DSL for working +with hours, minutes, and seconds--these last two in base 60. + +``H`` is an alias for the ``Hours`` class:: + + >>> H[1] + 1:00 + >>> H[1:30] + 1:30 + >>> H[1::5] + 1:00:05 + >>> H[::5] + 0:00:05 + +An ``H`` instance can be converted to a float number of hours:: + + >>> float(H[1:15]) + 1.25 + >>> float(H[1:30:30]) # doctest: +ELLIPSIS + 1.5083333... + >>> float(H[1::5]) # doctest: +ELLIPSIS + 1.0013888... + +The ``H`` constructor accepts hours, minutes, and/or seconds:: + + >>> H(1.5) + 1:30 + >>> H(1.9) + 1:54 + >>> H(1, 30, 30) + 1:30:30 + >>> H(s = 7205) + 2:00:05 + >>> H(1/3) + 0:20 + >>> H(1/1000) + 0:00:03.6 + +An ``H`` instance is iterable, for convenient unpacking:: + + >>> hms = H[1:22:33] + >>> h, m, s = hms + >>> h, m, s + (1, 22, 33) + >>> tuple(hms) + (1, 22, 33) + + +``H`` instances can be added:: + + >>> H[1:45:12] + H[2:15:50] + 4:01:02 +""" + +from typing import Tuple, Union + + +def normalize(s: float) -> Tuple[int, int, float]: + h, r = divmod(s, 3600) + m, s = divmod(r, 60) + return int(h), int(m), s + + +def valid_base_60(n, unit): + if not (0 <= n < 60): + raise ValueError(f'invalid {unit} {n}') + return n + + +class Hours: + h: int + _m: int + _s: float + + def __class_getitem__(cls, parts: Union[slice, float]) -> 'Hours': + if isinstance(parts, slice): + h = parts.start or 0 + m = valid_base_60(parts.stop or 0, 'minutes') + s = valid_base_60(parts.step or 0, 'seconds') + else: + h, m, s = normalize(parts * 3600) + return Hours(h, m, s) + + def __init__(self, h: float = 0, m: float = 0, s: float = 0): + if h < 0 or m < 0 or s < 0: + raise ValueError('invalid negative argument') + self.h, self.m, self.s = normalize(h * 3600 + m * 60 + s) + + def __repr__(self): + h, m, s = self + display_s = f'{s:06.3f}' + display_s = display_s.rstrip('0').rstrip('.') + if display_s == '00': + return f'{h}:{m:02d}' + return f'{h}:{m:02d}:{display_s}' + + def __float__(self): + return self.h + self.m / 60 + self.s / 3600 + + def __eq__(self, other): + return repr(self) == repr(other) + + def __iter__(self): + yield self.h + yield self.m + yield self.s + + def __add__(self, other): + if not isinstance(other, Hours): + return NotImplemented + return Hours(*(a + b for a, b in zip(self, other))) + + +H = Hours diff --git a/25-class-metaprog/hours/hours_test.py b/25-class-metaprog/hours/hours_test.py new file mode 100644 index 0000000..dc57e3f --- /dev/null +++ b/25-class-metaprog/hours/hours_test.py @@ -0,0 +1,71 @@ +# content of test_expectation.py +from math import isclose + +import pytest + +from hours import normalize, H + +HOURS_TO_HMS = [ + [1, (1, 0, 0.0)], + [1.5, (1, 30, 0.0)], + [1.1, (1, 6, 0.0)], + [1.9, (1, 54, 0.0)], + [1.01, (1, 0, 36.0)], + [1.09, (1, 5, 24.0)], + [2 + 1/60, (2, 1, 0.0)], + [3 + 1/3600, (3, 0, 1.0)], + [1.251, (1, 15, 3.6)], +] + + +@pytest.mark.parametrize('hours, expected', HOURS_TO_HMS) +def test_normalize(hours, expected): + h, m, s = expected + got_h, got_m, got_s = normalize(hours * 3600) + assert (h, m) == (got_h, got_m) + assert isclose(s, got_s, abs_tol=1e-12) + got_hours = got_h + got_m / 60 + got_s / 3600 + assert isclose(hours, got_hours) + + +@pytest.mark.parametrize('h, expected', [ + (H[1], '1:00'), + (H[1:0], '1:00'), + (H[1:3], '1:03'), + (H[1:59], '1:59'), + (H[1:0:0], '1:00'), + (H[1:2:3], '1:02:03'), + (H[1:2:3.4], '1:02:03.4'), + (H[1:2:0.1], '1:02:00.1'), + (H[1:2:0.01], '1:02:00.01'), + (H[1:2:0.001], '1:02:00.001'), + (H[1:2:0.0001], '1:02'), +]) +def test_repr(h, expected): + assert expected == repr(h), f'seconds: {h.s}' + + +@pytest.mark.parametrize('expected, hms', HOURS_TO_HMS) +def test_float(expected, hms): + got = float(H[slice(*hms)]) + assert isclose(expected, got) + + +@pytest.mark.parametrize('hms, units', [ + ((0, 60, 0), 'minutes'), + ((0, 0, 60), 'seconds'), + ((0, 60, 60), 'minutes'), +]) +def test_class_getitem_errors(hms, units): + with pytest.raises(ValueError) as excinfo: + H[slice(*hms)] + assert units in str(excinfo.value) + + +@pytest.mark.parametrize('hms1, hms2, expected', [ + (H[0:30], H[0:15], H[0:45]), + (H[0:30], H[0:30], H[1:00]), + (H[0:59:59], H[0:00:1], H[1:00]), +]) +def test_add(hms1, hms2, expected): + assert expected == hms1 + hms2 diff --git a/25-class-metaprog/metabunch/README.md b/25-class-metaprog/metabunch/README.md new file mode 100644 index 0000000..e6cf753 --- /dev/null +++ b/25-class-metaprog/metabunch/README.md @@ -0,0 +1,20 @@ +# Examples from Python in a Nutshell, 3rd edition + +The metaclass `MetaBunch` example in `original/bunch.py` is an exact copy of the +last example in the _How a Metaclass Creates a Class_ section of +_Chapter 4: Object Oriented Python_ from +[_Python in a Nutshell, 3rd edition_](https://learning.oreilly.com/library/view/python-in-a/9781491913833) +by Alex Martelli, Anna Ravenscroft, and Steve Holden. + +The version in `pre3.6/bunch.py` is slightly simplified by taking advantage +of Python 3 `super()` and removing comments and docstrings, +to make it easier to compare to the `from3.6` version. + +The version in `from3.6/bunch.py` is further simplified by taking advantage +of the order-preserving `dict` that appeared in Python 3.6, +as well as other simplifications, +such as leveraging closures in `__init__` and `__repr__` +to avoid adding a `__defaults__` mapping to the class. + +The external behavior of all three versions is the same, and +the test files `bunch_test.py` are identical in the three directories. diff --git a/25-class-metaprog/metabunch/from3.6/bunch.py b/25-class-metaprog/metabunch/from3.6/bunch.py new file mode 100644 index 0000000..a8b3d5a --- /dev/null +++ b/25-class-metaprog/metabunch/from3.6/bunch.py @@ -0,0 +1,77 @@ +""" +The `MetaBunch` metaclass is a simplified version of the +last example in the _How a Metaclass Creates a Class_ section +of _Chapter 4: Object Oriented Python_ from +[_Python in a Nutshell, 3rd edition_](https://learning.oreilly.com/library/view/python-in-a/9781491913833) +by Alex Martelli, Anna Ravenscroft, and Steve Holden. + +Here are a few tests. ``bunch_test.py`` has a few more. + +# tag::BUNCH_POINT_DEMO_1[] + >>> class Point(Bunch): + ... x = 0.0 + ... y = 0.0 + ... color = 'gray' + ... + >>> Point(x=1.2, y=3, color='green') + Point(x=1.2, y=3, color='green') + >>> p = Point() + >>> p.x, p.y, p.color + (0.0, 0.0, 'gray') + >>> p + Point() + +# end::BUNCH_POINT_DEMO_1[] + +# tag::BUNCH_POINT_DEMO_2[] + + >>> Point(x=1, y=2, z=3) + Traceback (most recent call last): + ... + AttributeError: 'Point' object has no attribute 'z' + >>> p = Point(x=21) + >>> p.y = 42 + >>> p + Point(x=21, y=42) + >>> p.flavor = 'banana' + Traceback (most recent call last): + ... + AttributeError: 'Point' object has no attribute 'flavor' + +# end::BUNCH_POINT_DEMO_2[] +""" + +# tag::METABUNCH[] +class MetaBunch(type): # <1> + def __new__(meta_cls, cls_name, bases, cls_dict): # <2> + + defaults = {} # <3> + + def __init__(self, **kwargs): # <4> + for name, default in defaults.items(): # <5> + setattr(self, name, kwargs.pop(name, default)) + if kwargs: # <6> + setattr(self, *kwargs.popitem()) + + def __repr__(self): # <7> + rep = ', '.join(f'{name}={value!r}' + for name, default in defaults.items() + if (value := getattr(self, name)) != default) + return f'{cls_name}({rep})' + + new_dict = dict(__slots__=[], __init__=__init__, __repr__=__repr__) # <8> + + for name, value in cls_dict.items(): # <9> + if name.startswith('__') and name.endswith('__'): # <10> + if name in new_dict: + raise AttributeError(f"Can't set {name!r} in {cls_name!r}") + new_dict[name] = value + else: # <11> + new_dict['__slots__'].append(name) + defaults[name] = value + return super().__new__(meta_cls, cls_name, bases, new_dict) # <12> + + +class Bunch(metaclass=MetaBunch): # <13> + pass +# end::METABUNCH[] diff --git a/25-class-metaprog/metabunch/from3.6/bunch_test.py b/25-class-metaprog/metabunch/from3.6/bunch_test.py new file mode 100644 index 0000000..322487a --- /dev/null +++ b/25-class-metaprog/metabunch/from3.6/bunch_test.py @@ -0,0 +1,59 @@ +import pytest + +from bunch import Bunch + +class Point(Bunch): + """ A point has x and y coordinates, defaulting to 0.0, + and a color, defaulting to 'gray'—and nothing more, + except what Python and the metaclass conspire to add, + such as __init__ and __repr__ + """ + x = 0.0 + y = 0.0 + color = 'gray' + + +def test_init_defaults(): + p = Point() + assert repr(p) == 'Point()' + + +def test_init(): + p = Point(x=1.2, y=3.4, color='red') + assert repr(p) == "Point(x=1.2, y=3.4, color='red')" + + +def test_init_wrong_argument(): + with pytest.raises(AttributeError) as exc: + p = Point(x=1.2, y=3.4, flavor='coffee') + assert "no attribute 'flavor'" in str(exc.value) + + +def test_slots(): + p = Point() + with pytest.raises(AttributeError) as exc: + p.z = 5.6 + assert "no attribute 'z'" in str(exc.value) + + +def test_dunder_permitted(): + class Cat(Bunch): + name = '' + weight = 0 + + def __str__(self): + return f'{self.name} ({self.weight} kg)' + + cheshire = Cat(name='Cheshire') + assert str(cheshire) == 'Cheshire (0 kg)' + + +def test_dunder_forbidden(): + with pytest.raises(AttributeError) as exc: + class Cat(Bunch): + name = '' + weight = 0 + + def __init__(self): + pass + assert "Can't set '__init__' in 'Cat'" in str(exc.value) diff --git a/25-class-metaprog/metabunch/nutshell3e/bunch.py b/25-class-metaprog/metabunch/nutshell3e/bunch.py new file mode 100644 index 0000000..b6f09cb --- /dev/null +++ b/25-class-metaprog/metabunch/nutshell3e/bunch.py @@ -0,0 +1,85 @@ +import collections +import warnings + +class MetaBunch(type): + """ + Metaclass for new and improved "Bunch": implicitly defines + __slots__, __init__ and __repr__ from variables bound in + class scope. + A class statement for an instance of MetaBunch (i.e., for a + class whose metaclass is MetaBunch) must define only + class-scope data attributes (and possibly special methods, but + NOT __init__ and __repr__). MetaBunch removes the data + attributes from class scope, snuggles them instead as items in + a class-scope dict named __dflts__, and puts in the class a + __slots__ with those attributes' names, an __init__ that takes + as optional named arguments each of them (using the values in + __dflts__ as defaults for missing ones), and a __repr__ that + shows the repr of each attribute that differs from its default + value (the output of __repr__ can be passed to __eval__ to make + an equal instance, as per usual convention in the matter, if + each non-default-valued attribute respects the convention too). + + In v3, the order of data attributes remains the same as in the + class body; in v2, there is no such guarantee. + """ + def __prepare__(name, *bases, **kwargs): + # precious in v3—harmless although useless in v2 + return collections.OrderedDict() + + def __new__(mcl, classname, bases, classdict): + """ Everything needs to be done in __new__, since + type.__new__ is where __slots__ are taken into account. + """ + # define as local functions the __init__ and __repr__ that + # we'll use in the new class + def __init__(self, **kw): + """ Simplistic __init__: first set all attributes to + default values, then override those explicitly + passed in kw. + """ + for k in self.__dflts__: + setattr(self, k, self.__dflts__[k]) + for k in kw: + setattr(self, k, kw[k]) + def __repr__(self): + """ Clever __repr__: show only attributes that differ + from default values, for compactness. + """ + rep = ['{}={!r}'.format(k, getattr(self, k)) + for k in self.__dflts__ + if getattr(self, k) != self.__dflts__[k] + ] + return '{}({})'.format(classname, ', '.join(rep)) + # build the newdict that we'll use as class-dict for the + # new class + newdict = { '__slots__':[], + '__dflts__':collections.OrderedDict(), + '__init__':__init__, '__repr__':__repr__, } + for k in classdict: + if k.startswith('__') and k.endswith('__'): + # dunder methods: copy to newdict, or warn + # about conflicts + if k in newdict: + warnings.warn( + "Can't set attr {!r} in bunch-class {!r}". + format(k, classname)) + else: + newdict[k] = classdict[k] + else: + # class variables, store name in __slots__, and + # name and value as an item in __dflts__ + newdict['__slots__'].append(k) + newdict['__dflts__'][k] = classdict[k] + # finally delegate the rest of the work to type.__new__ + return super(MetaBunch, mcl).__new__( + mcl, classname, bases, newdict) + +class Bunch(metaclass=MetaBunch): + """ For convenience: inheriting from Bunch can be used to get + the new metaclass (same as defining metaclass= yourself). + + In v2, remove the (metaclass=MetaBunch) above and add + instead __metaclass__=MetaBunch as the class body. + """ + pass diff --git a/25-class-metaprog/metabunch/nutshell3e/bunch_test.py b/25-class-metaprog/metabunch/nutshell3e/bunch_test.py new file mode 100644 index 0000000..00bbcd2 --- /dev/null +++ b/25-class-metaprog/metabunch/nutshell3e/bunch_test.py @@ -0,0 +1,38 @@ +import pytest + +from bunch import Bunch + +class Point(Bunch): + """ A point has x and y coordinates, defaulting to 0.0, + and a color, defaulting to 'gray'—and nothing more, + except what Python and the metaclass conspire to add, + such as __init__ and __repr__ + """ + x = 0.0 + y = 0.0 + color = 'gray' + + +def test_init_defaults(): + p = Point() + assert repr(p) == 'Point()' + + +def test_init(): + p = Point(x=1.2, y=3.4, color='red') + assert repr(p) == "Point(x=1.2, y=3.4, color='red')" + + +def test_init_wrong_argument(): + with pytest.raises(AttributeError) as exc: + p = Point(x=1.2, y=3.4, flavor='coffee') + assert "no attribute 'flavor'" in str(exc.value) + + +def test_slots(): + p = Point() + with pytest.raises(AttributeError) as exc: + p.z = 5.6 + assert "no attribute 'z'" in str(exc.value) + + diff --git a/25-class-metaprog/metabunch/original/bunch.py b/25-class-metaprog/metabunch/original/bunch.py new file mode 100644 index 0000000..f58a5ec --- /dev/null +++ b/25-class-metaprog/metabunch/original/bunch.py @@ -0,0 +1,70 @@ +import warnings + +class metaMetaBunch(type): + """ + metaclass for new and improved "Bunch": implicitly defines + __slots__, __init__ and __repr__ from variables bound in class scope. + + An instance of metaMetaBunch (a class whose metaclass is metaMetaBunch) + defines only class-scope variables (and possibly special methods, but + NOT __init__ and __repr__!). metaMetaBunch removes those variables from + class scope, snuggles them instead as items in a class-scope dict named + __dflts__, and puts in the class a __slots__ listing those variables' + names, an __init__ that takes as optional keyword arguments each of + them (using the values in __dflts__ as defaults for missing ones), and + a __repr__ that shows the repr of each attribute that differs from its + default value (the output of __repr__ can be passed to __eval__ to make + an equal instance, as per the usual convention in the matter). + """ + + def __new__(cls, classname, bases, classdict): + """ Everything needs to be done in __new__, since type.__new__ is + where __slots__ are taken into account. + """ + + # define as local functions the __init__ and __repr__ that we'll + # use in the new class + + def __init__(self, **kw): + """ Simplistic __init__: first set all attributes to default + values, then override those explicitly passed in kw. + """ + for k in self.__dflts__: setattr(self, k, self.__dflts__[k]) + for k in kw: setattr(self, k, kw[k]) + + def __repr__(self): + """ Clever __repr__: show only attributes that differ from the + respective default values, for compactness. + """ + rep = [ '%s=%r' % (k, getattr(self, k)) for k in self.__dflts__ + if getattr(self, k) != self.__dflts__[k] + ] + return '%s(%s)' % (classname, ', '.join(rep)) + + # build the newdict that we'll use as class-dict for the new class + newdict = { '__slots__':[], '__dflts__':{}, + '__init__':__init__, '__repr__':__repr__, } + + for k in classdict: + if k.startswith('__'): + # special methods &c: copy to newdict, warn about conflicts + if k in newdict: + warnings.warn("Can't set attr %r in bunch-class %r" % ( + k, classname)) + else: + newdict[k] = classdict[k] + else: + # class variables, store name in __slots__ and name and + # value as an item in __dflts__ + newdict['__slots__'].append(k) + newdict['__dflts__'][k] = classdict[k] + + # finally delegate the rest of the work to type.__new__ + return type.__new__(cls, classname, bases, newdict) + + +class MetaBunch(metaclass=metaMetaBunch): + """ For convenience: inheriting from MetaBunch can be used to get + the new metaclass (same as defining __metaclass__ yourself). + """ + __metaclass__ = metaMetaBunch diff --git a/25-class-metaprog/metabunch/original/bunch_test.py b/25-class-metaprog/metabunch/original/bunch_test.py new file mode 100644 index 0000000..174f58d --- /dev/null +++ b/25-class-metaprog/metabunch/original/bunch_test.py @@ -0,0 +1,38 @@ +import pytest + +from bunch import MetaBunch + +class Point(MetaBunch): + """ A point has x and y coordinates, defaulting to 0.0, + and a color, defaulting to 'gray'—and nothing more, + except what Python and the metaclass conspire to add, + such as __init__ and __repr__ + """ + x = 0.0 + y = 0.0 + color = 'gray' + + +def test_init_defaults(): + p = Point() + assert repr(p) == 'Point()' + + +def test_init(): + p = Point(x=1.2, y=3.4, color='red') + assert repr(p) == "Point(x=1.2, y=3.4, color='red')" + + +def test_init_wrong_argument(): + with pytest.raises(AttributeError) as exc: + p = Point(x=1.2, y=3.4, flavor='coffee') + assert "no attribute 'flavor'" in str(exc.value) + + +def test_slots(): + p = Point() + with pytest.raises(AttributeError) as exc: + p.z = 5.6 + assert "no attribute 'z'" in str(exc.value) + + diff --git a/25-class-metaprog/metabunch/pre3.6/bunch.py b/25-class-metaprog/metabunch/pre3.6/bunch.py new file mode 100644 index 0000000..186d645 --- /dev/null +++ b/25-class-metaprog/metabunch/pre3.6/bunch.py @@ -0,0 +1,41 @@ +import collections +import warnings + +class MetaBunch(type): + def __prepare__(name, *bases, **kwargs): + return collections.OrderedDict() + + def __new__(meta_cls, cls_name, bases, cls_dict): + def __init__(self, **kw): + for k in self.__defaults__: + setattr(self, k, self.__defaults__[k]) + for k in kw: + setattr(self, k, kw[k]) + + def __repr__(self): + rep = ['{}={!r}'.format(k, getattr(self, k)) + for k in self.__defaults__ + if getattr(self, k) != self.__defaults__[k] + ] + return '{}({})'.format(cls_name, ', '.join(rep)) + + new_dict = { '__slots__':[], + '__defaults__':collections.OrderedDict(), + '__init__':__init__, '__repr__':__repr__, } + + for k in cls_dict: + if k.startswith('__') and k.endswith('__'): + if k in new_dict: + warnings.warn( + "Can't set attr {!r} in bunch-class {!r}". + format(k, cls_name)) + else: + new_dict[k] = cls_dict[k] + else: + new_dict['__slots__'].append(k) + new_dict['__defaults__'][k] = cls_dict[k] + + return super().__new__(meta_cls, cls_name, bases, new_dict) + +class Bunch(metaclass=MetaBunch): + pass diff --git a/25-class-metaprog/metabunch/pre3.6/bunch_test.py b/25-class-metaprog/metabunch/pre3.6/bunch_test.py new file mode 100644 index 0000000..00bbcd2 --- /dev/null +++ b/25-class-metaprog/metabunch/pre3.6/bunch_test.py @@ -0,0 +1,38 @@ +import pytest + +from bunch import Bunch + +class Point(Bunch): + """ A point has x and y coordinates, defaulting to 0.0, + and a color, defaulting to 'gray'—and nothing more, + except what Python and the metaclass conspire to add, + such as __init__ and __repr__ + """ + x = 0.0 + y = 0.0 + color = 'gray' + + +def test_init_defaults(): + p = Point() + assert repr(p) == 'Point()' + + +def test_init(): + p = Point(x=1.2, y=3.4, color='red') + assert repr(p) == "Point(x=1.2, y=3.4, color='red')" + + +def test_init_wrong_argument(): + with pytest.raises(AttributeError) as exc: + p = Point(x=1.2, y=3.4, flavor='coffee') + assert "no attribute 'flavor'" in str(exc.value) + + +def test_slots(): + p = Point() + with pytest.raises(AttributeError) as exc: + p.z = 5.6 + assert "no attribute 'z'" in str(exc.value) + + diff --git a/25-class-metaprog/persistent/persistlib.py b/25-class-metaprog/persistent/persistlib.py index 15c9d38..0179ca3 100644 --- a/25-class-metaprog/persistent/persistlib.py +++ b/25-class-metaprog/persistent/persistlib.py @@ -4,16 +4,16 @@ >>> class Movie(Persistent): ... title: str ... year: int - ... megabucks: float + ... box_office: float Implemented behavior:: >>> Movie._connect() # doctest: +ELLIPSIS - >>> movie = Movie(title='The Godfather', year=1972, megabucks=137) + >>> movie = Movie(title='The Godfather', year=1972, box_office=137) >>> movie.title 'The Godfather' - >>> movie.megabucks + >>> movie.box_office 137.0 Instances always have a ``._pk`` attribute, but it is ``None`` until the @@ -32,7 +32,7 @@ >>> del movie >>> film = Movie[1] >>> film - Movie(title='The Godfather', year=1972, megabucks=137.0, _pk=1) + Movie(title='The Godfather', year=1972, box_office=137.0, _pk=1) By default, the table name is the class name lowercased, with an appended "s" for plural:: @@ -84,35 +84,12 @@ def _fields(cls) -> dict[str, type]: if not name.startswith('_') } - def __init_subclass__(cls, *, table: str = '', **kwargs: dict): + def __init_subclass__(cls, *, table: str = '', **kwargs: Any): super().__init_subclass__(**kwargs) # type:ignore cls._TABLE_NAME = table if table else cls.__name__.lower() + 's' for name, py_type in cls._fields().items(): setattr(cls, name, Field(name, py_type)) - @staticmethod - def _connect(db_path: str = db.DEFAULT_DB_PATH): - return db.connect(db_path) - - @classmethod - def _ensure_table(cls) -> str: - if not cls._TABLE_READY: - db.ensure_table(cls._TABLE_NAME, cls._fields()) - cls._TABLE_READY = True - return cls._TABLE_NAME - - def __class_getitem__(cls, pk: int) -> 'Persistent': - field_names = ['_pk'] + list(cls._fields()) - values = db.fetch_record(cls._TABLE_NAME, pk) - return cls(**dict(zip(field_names, values))) - - def _asdict(self) -> dict[str, Any]: - return { - name: getattr(self, name) - for name, attr in self.__class__.__dict__.items() - if isinstance(attr, Field) - } - def __init__(self, *, _pk=None, **kwargs): field_names = self._asdict().keys() for name, arg in kwargs.items(): @@ -131,6 +108,32 @@ def __repr__(self) -> str: return f'{cls_name}({kwargs})' return f'{cls_name}({kwargs}, _pk={self._pk})' + def _asdict(self) -> dict[str, Any]: + return { + name: getattr(self, name) + for name, attr in self.__class__.__dict__.items() + if isinstance(attr, Field) + } + + + # database methods + + @staticmethod + def _connect(db_path: str = db.DEFAULT_DB_PATH): + return db.connect(db_path) + + @classmethod + def _ensure_table(cls) -> str: + if not cls._TABLE_READY: + db.ensure_table(cls._TABLE_NAME, cls._fields()) + cls._TABLE_READY = True + return cls._TABLE_NAME + + def __class_getitem__(cls, pk: int) -> 'Persistent': + field_names = ['_pk'] + list(cls._fields()) + values = db.fetch_record(cls._TABLE_NAME, pk) + return cls(**dict(zip(field_names, values))) + def _save(self) -> int: table = self.__class__._ensure_table() if self._pk is None: diff --git a/25-class-metaprog/qualname/fakedjango.py b/25-class-metaprog/qualname/fakedjango.py new file mode 100644 index 0000000..76a68eb --- /dev/null +++ b/25-class-metaprog/qualname/fakedjango.py @@ -0,0 +1,5 @@ +class models: + class Model: + "nothing to see here" + class IntegerField: + "nothing to see here" diff --git a/25-class-metaprog/qualname/models.py b/25-class-metaprog/qualname/models.py new file mode 100755 index 0000000..6045703 --- /dev/null +++ b/25-class-metaprog/qualname/models.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +from fakedjango import models + +class Ox(models.Model): + horn_length = models.IntegerField() + + class Meta: + ordering = ['horn_length'] + verbose_name_plural = 'oxen' + +print(Ox.Meta.__name__) +print(Ox.Meta.__qualname__) diff --git a/25-class-metaprog/setattr/example_from_leo.py b/25-class-metaprog/setattr/example_from_leo.py new file mode 100755 index 0000000..980fb55 --- /dev/null +++ b/25-class-metaprog/setattr/example_from_leo.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 + +class Foo: + @property + def bar(self): + return self._bar + + @bar.setter + def bar(self, value): + self._bar = value + + def __setattr__(self, name, value): + print(f'setting {name!r} to {value!r}') + super().__setattr__(name, value) + +o = Foo() +o.bar = 8 +print(o.bar) +print(o._bar) diff --git a/25-class-metaprog/slots/slots_timing.py b/25-class-metaprog/slots/slots_timing.py new file mode 100755 index 0000000..ef7d941 --- /dev/null +++ b/25-class-metaprog/slots/slots_timing.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +class Wrong: + + def __init_subclass__(subclass): + subclass.__slots__ = ('x', 'y') + + +class Klass0(Wrong): + pass + + +o = Klass0() +o.z = 3 +print('o.z = 3 # did not raise Attribute error because __slots__ was created too late') + + +class Correct1(type): + + def __new__(meta_cls, cls_name, bases, cls_dict): + cls_dict['__slots__'] = ('x', 'y') + return super().__new__( + meta_cls, cls_name, bases, cls_dict) + + +class Klass1(metaclass=Correct1): + pass + +o = Klass1() +try: + o.z = 3 +except AttributeError as e: + print('Raised as expected:', e) + + +class Correct2(type): + def __prepare__(name, bases): + return dict(__slots__=('x', 'y')) + +class Klass2(metaclass=Correct2): + pass + +o = Klass2() +try: + o.z = 3 +except AttributeError as e: + print('Raised as expected:', e) + diff --git a/25-class-metaprog/tinyenums/microenum.py b/25-class-metaprog/tinyenums/microenum.py index 873d469..26aea64 100644 --- a/25-class-metaprog/tinyenums/microenum.py +++ b/25-class-metaprog/tinyenums/microenum.py @@ -2,9 +2,9 @@ # shared privately with me, with permission to use in Fluent Python 2e. """ -Testing ``AutoFillDict``:: +Testing ``WilyDict``:: - >>> adict = AutoFillDict() + >>> adict = WilyDict() >>> len(adict) 0 >>> adict['first'] @@ -37,7 +37,7 @@ """ -class AutoFillDict(dict): +class WilyDict(dict): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.__next_value = 0 @@ -52,12 +52,14 @@ def __missing__(self, key): class MicroEnumMeta(type): def __prepare__(name, bases, **kwargs): - return AutoFillDict() + return WilyDict() - -class MicroEnum(metaclass=MicroEnumMeta): - def __class_getitem__(cls, key): + def __getitem__(cls, key): for k, v in cls.__dict__.items(): if v == key: return k raise KeyError(key) + + +class MicroEnum(metaclass=MicroEnumMeta): + pass diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..df3eb51 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts = --doctest-modules From f248baf418c49e5748bd503dd992b52440ed236b Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sun, 23 May 2021 22:12:13 -0300 Subject: [PATCH 017/127] adding sentinel metaclass example --- 08-def-type-hints/typevars_constrained.py | 26 +++++++++ 15-more-types/{ => cafeteria}/cafeteria.py | 2 +- 15-more-types/cafeteria/contravariant.py | 45 ++++++++++++++++ 15-more-types/cafeteria/covariant.py | 48 +++++++++++++++++ 15-more-types/cafeteria/invariant.py | 54 +++++++++++++++++++ .../clip_annot.py | 0 .../clip_annot_1ed.py | 0 .../clip_annot_signature.rst | 0 .../mymax}/mymax.py | 0 .../mymax}/mymax_demo.py | 0 .../mymax}/mymax_test.py | 0 {08-def-type-hints => 15-more-types}/mysum.py | 0 25-class-metaprog/sentinel/sentinel.py | 24 +++++++++ 25-class-metaprog/sentinel/sentinel_test.py | 22 ++++++++ 14 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 08-def-type-hints/typevars_constrained.py rename 15-more-types/{ => cafeteria}/cafeteria.py (98%) create mode 100644 15-more-types/cafeteria/contravariant.py create mode 100644 15-more-types/cafeteria/covariant.py create mode 100644 15-more-types/cafeteria/invariant.py rename {08-def-type-hints => 15-more-types}/clip_annot.py (100%) rename {08-def-type-hints => 15-more-types}/clip_annot_1ed.py (100%) rename {08-def-type-hints => 15-more-types}/clip_annot_signature.rst (100%) rename {08-def-type-hints/comparable => 15-more-types/mymax}/mymax.py (100%) rename {08-def-type-hints/comparable => 15-more-types/mymax}/mymax_demo.py (100%) rename {08-def-type-hints/comparable => 15-more-types/mymax}/mymax_test.py (100%) rename {08-def-type-hints => 15-more-types}/mysum.py (100%) create mode 100644 25-class-metaprog/sentinel/sentinel.py create mode 100644 25-class-metaprog/sentinel/sentinel_test.py diff --git a/08-def-type-hints/typevars_constrained.py b/08-def-type-hints/typevars_constrained.py new file mode 100644 index 0000000..2bfea13 --- /dev/null +++ b/08-def-type-hints/typevars_constrained.py @@ -0,0 +1,26 @@ +from typing import TypeVar, TYPE_CHECKING +from decimal import Decimal + +# tag::TYPEVAR_RESTRICTED[] +RT = TypeVar('RT', float, Decimal) + +def triple1(a: RT) -> RT: + return a * 3 + +res1 = triple1(1, 2) + +if TYPE_CHECKING: + reveal_type(res1) +# end::TYPEVAR_RESTRICTED[] + +# tag::TYPEVAR_BOUNDED[] +BT = TypeVar('BT', bound=float) + +def triple2(a: BT) -> BT: + return a * 3 + +res2 = triple2(1, 2) + +if TYPE_CHECKING: + reveal_type(res2) +# tag::TYPEVAR_BOUNDED[] diff --git a/15-more-types/cafeteria.py b/15-more-types/cafeteria/cafeteria.py similarity index 98% rename from 15-more-types/cafeteria.py rename to 15-more-types/cafeteria/cafeteria.py index 622f8e4..36769df 100644 --- a/15-more-types/cafeteria.py +++ b/15-more-types/cafeteria/cafeteria.py @@ -41,7 +41,7 @@ class Compostable(Biodegradable): class TrashCan(Generic[T_contra]): def put(self, trash: T_contra) -> None: - """Store trash until dumped...""" + """Store trash until dumped.""" class Cafeteria: diff --git a/15-more-types/cafeteria/contravariant.py b/15-more-types/cafeteria/contravariant.py new file mode 100644 index 0000000..399a748 --- /dev/null +++ b/15-more-types/cafeteria/contravariant.py @@ -0,0 +1,45 @@ +# tag::TRASH_TYPES[] +from typing import TypeVar, Generic + +class Refuse: # <1> + """Any refuse.""" + +class Biodegradable(Refuse): + """Biodegradable refuse.""" + +class Compostable(Biodegradable): + """Compostable refuse.""" + +T_contra = TypeVar('T_contra', contravariant=True) # <2> + +class TrashCan(Generic[T_contra]): # <3> + def put(self, refuse: T_contra) -> None: + """Store trash until dumped.""" + +def deploy(trash_can: TrashCan[Biodegradable]): + """Deploy a trash can for biodegradable refuse.""" +# end::TRASH_TYPES[] + + +################################################ contravariant trash can + + +# tag::DEPLOY_TRASH_CANS[] +bio_can: TrashCan[Biodegradable] = TrashCan() +deploy(bio_can) + +trash_can: TrashCan[Refuse] = TrashCan() +deploy(trash_can) +# end::DEPLOY_TRASH_CANS[] + + +################################################ more specific trash can + +# tag::DEPLOY_NOT_VALID[] +compost_can: TrashCan[Compostable] = TrashCan() +deploy(compost_can) +# end::DEPLOY_NOT_VALID[] + +## Argument 1 to "deploy" has +## incompatible type "TrashCan[Compostable]" +## expected "TrashCan[Biodegradable]" diff --git a/15-more-types/cafeteria/covariant.py b/15-more-types/cafeteria/covariant.py new file mode 100644 index 0000000..87879dd --- /dev/null +++ b/15-more-types/cafeteria/covariant.py @@ -0,0 +1,48 @@ +from typing import TypeVar, Generic + + +class Beverage: + """Any beverage.""" + + +class Juice(Beverage): + """Any fruit juice.""" + + +class OrangeJuice(Juice): + """Delicious juice from Brazilian oranges.""" + + +# tag::BEVERAGE_TYPES[] +T_co = TypeVar('T_co', covariant=True) # <1> + + +class BeverageDispenser(Generic[T_co]): # <2> + def __init__(self, beverage: T_co) -> None: + self.beverage = beverage + + def dispense(self) -> T_co: + return self.beverage + +def install(dispenser: BeverageDispenser[Juice]) -> None: # <3> + """Install a fruit juice dispenser.""" +# end::BEVERAGE_TYPES[] + +################################################ covariant dispenser + +# tag::INSTALL_JUICE_DISPENSERS[] +juice_dispenser = BeverageDispenser(Juice()) +install(juice_dispenser) + +orange_juice_dispenser = BeverageDispenser(OrangeJuice()) +install(orange_juice_dispenser) +# end::INSTALL_JUICE_DISPENSERS[] + +################################################ not a juice dispenser + +beverage_dispenser = BeverageDispenser(Beverage()) + +## Argument 1 to "install" has +## incompatible type "BeverageDispenser[Beverage]" +## expected "BeverageDispenser[Juice]" +install(beverage_dispenser) diff --git a/15-more-types/cafeteria/invariant.py b/15-more-types/cafeteria/invariant.py new file mode 100644 index 0000000..1f9d6f6 --- /dev/null +++ b/15-more-types/cafeteria/invariant.py @@ -0,0 +1,54 @@ +# tag::BEVERAGE_TYPES[] +from typing import TypeVar, Generic + +class Beverage: # <1> + """Any beverage.""" + +class Juice(Beverage): + """Any fruit juice.""" + +class OrangeJuice(Juice): + """Delicious juice from Brazilian oranges.""" + +T = TypeVar('T') # <2> + +class BeverageDispenser(Generic[T]): # <3> + """A dispenser parameterized on the beverage type.""" + def __init__(self, beverage: T) -> None: + self.beverage = beverage + + def dispense(self) -> T: + return self.beverage + +def install(dispenser: BeverageDispenser[Juice]) -> None: # <4> + """Install a fruit juice dispenser.""" +# end::BEVERAGE_TYPES[] + +################################################ exact type + +# tag::INSTALL_JUICE_DISPENSER[] +juice_dispenser = BeverageDispenser(Juice()) +install(juice_dispenser) +# end::INSTALL_JUICE_DISPENSER[] + + +################################################ variant dispenser + +# tag::INSTALL_BEVERAGE_DISPENSER[] +beverage_dispenser = BeverageDispenser(Beverage()) +install(beverage_dispenser) +## Argument 1 to "install" has +## incompatible type "BeverageDispenser[Beverage]" +## expected "BeverageDispenser[Juice]" +# end::INSTALL_BEVERAGE_DISPENSER[] + + +################################################ variant dispenser + +# tag::INSTALL_ORANGE_JUICE_DISPENSER[] +orange_juice_dispenser = BeverageDispenser(OrangeJuice()) +install(orange_juice_dispenser) +# end::INSTALL_ORANGE_JUICE_DISPENSER[] +## Argument 1 to "install" has +## incompatible type "BeverageDispenser[OrangeJuice]" +## expected "BeverageDispenser[Juice]" diff --git a/08-def-type-hints/clip_annot.py b/15-more-types/clip_annot.py similarity index 100% rename from 08-def-type-hints/clip_annot.py rename to 15-more-types/clip_annot.py diff --git a/08-def-type-hints/clip_annot_1ed.py b/15-more-types/clip_annot_1ed.py similarity index 100% rename from 08-def-type-hints/clip_annot_1ed.py rename to 15-more-types/clip_annot_1ed.py diff --git a/08-def-type-hints/clip_annot_signature.rst b/15-more-types/clip_annot_signature.rst similarity index 100% rename from 08-def-type-hints/clip_annot_signature.rst rename to 15-more-types/clip_annot_signature.rst diff --git a/08-def-type-hints/comparable/mymax.py b/15-more-types/mymax/mymax.py similarity index 100% rename from 08-def-type-hints/comparable/mymax.py rename to 15-more-types/mymax/mymax.py diff --git a/08-def-type-hints/comparable/mymax_demo.py b/15-more-types/mymax/mymax_demo.py similarity index 100% rename from 08-def-type-hints/comparable/mymax_demo.py rename to 15-more-types/mymax/mymax_demo.py diff --git a/08-def-type-hints/comparable/mymax_test.py b/15-more-types/mymax/mymax_test.py similarity index 100% rename from 08-def-type-hints/comparable/mymax_test.py rename to 15-more-types/mymax/mymax_test.py diff --git a/08-def-type-hints/mysum.py b/15-more-types/mysum.py similarity index 100% rename from 08-def-type-hints/mysum.py rename to 15-more-types/mysum.py diff --git a/25-class-metaprog/sentinel/sentinel.py b/25-class-metaprog/sentinel/sentinel.py new file mode 100644 index 0000000..bc94e5d --- /dev/null +++ b/25-class-metaprog/sentinel/sentinel.py @@ -0,0 +1,24 @@ +""" + + >>> class Missing(Sentinel): pass + >>> Missing + + >>> class CustomRepr(Sentinel): + ... repr = '*** sentinel ***' + ... + >>> CustomRepr + *** sentinel *** + +""" + +class SentinelMeta(type): + def __repr__(cls): + try: + return cls.repr + except AttributeError: + return f'<{cls.__name__}>' + +class Sentinel(metaclass=SentinelMeta): + def __new__(cls): + return cls + diff --git a/25-class-metaprog/sentinel/sentinel_test.py b/25-class-metaprog/sentinel/sentinel_test.py new file mode 100644 index 0000000..60a2919 --- /dev/null +++ b/25-class-metaprog/sentinel/sentinel_test.py @@ -0,0 +1,22 @@ +from sentinel import Sentinel + +class PlainSentinel(Sentinel): pass + + +class SentinelCustomRepr(Sentinel): + repr = '***SentinelRepr***' + + +def test_repr(): + assert repr(PlainSentinel) == '' + + +def test_pickle(): + from pickle import dumps, loads + s = dumps(PlainSentinel) + ps = loads(s) + assert ps is PlainSentinel + +def test_custom_repr(): + assert repr(SentinelCustomRepr) == '***SentinelRepr***' + \ No newline at end of file From be7a67c93e948a63e6b942a3347f7e1fbd33126a Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sun, 23 May 2021 22:19:15 -0300 Subject: [PATCH 018/127] change standard repr to ClassName --- 25-class-metaprog/sentinel/sentinel.py | 9 ++++----- 25-class-metaprog/sentinel/sentinel_test.py | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/25-class-metaprog/sentinel/sentinel.py b/25-class-metaprog/sentinel/sentinel.py index bc94e5d..20fcc7c 100644 --- a/25-class-metaprog/sentinel/sentinel.py +++ b/25-class-metaprog/sentinel/sentinel.py @@ -2,12 +2,12 @@ >>> class Missing(Sentinel): pass >>> Missing - + Missing >>> class CustomRepr(Sentinel): - ... repr = '*** sentinel ***' + ... repr = '' ... >>> CustomRepr - *** sentinel *** + """ @@ -16,9 +16,8 @@ def __repr__(cls): try: return cls.repr except AttributeError: - return f'<{cls.__name__}>' + return cls.__name__ class Sentinel(metaclass=SentinelMeta): def __new__(cls): return cls - diff --git a/25-class-metaprog/sentinel/sentinel_test.py b/25-class-metaprog/sentinel/sentinel_test.py index 60a2919..1a645fa 100644 --- a/25-class-metaprog/sentinel/sentinel_test.py +++ b/25-class-metaprog/sentinel/sentinel_test.py @@ -8,7 +8,7 @@ class SentinelCustomRepr(Sentinel): def test_repr(): - assert repr(PlainSentinel) == '' + assert repr(PlainSentinel) == 'PlainSentinel' def test_pickle(): From 8d882d9560e778bf1e797d0da37e547baf9bc3df Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sun, 23 May 2021 22:24:14 -0300 Subject: [PATCH 019/127] added blank line --- 25-class-metaprog/sentinel/sentinel_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/25-class-metaprog/sentinel/sentinel_test.py b/25-class-metaprog/sentinel/sentinel_test.py index 1a645fa..b328dae 100644 --- a/25-class-metaprog/sentinel/sentinel_test.py +++ b/25-class-metaprog/sentinel/sentinel_test.py @@ -17,6 +17,7 @@ def test_pickle(): ps = loads(s) assert ps is PlainSentinel + def test_custom_repr(): assert repr(SentinelCustomRepr) == '***SentinelRepr***' - \ No newline at end of file + From 493b0d2a565b42b53f51fd053e239220c54ea951 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sun, 23 May 2021 22:30:40 -0300 Subject: [PATCH 020/127] ch25: sentinel metaclass example --- 25-class-metaprog/sentinel/sentinel_test.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/25-class-metaprog/sentinel/sentinel_test.py b/25-class-metaprog/sentinel/sentinel_test.py index b328dae..4142289 100644 --- a/25-class-metaprog/sentinel/sentinel_test.py +++ b/25-class-metaprog/sentinel/sentinel_test.py @@ -1,3 +1,5 @@ +import pickle + from sentinel import Sentinel class PlainSentinel(Sentinel): pass @@ -12,12 +14,17 @@ def test_repr(): def test_pickle(): - from pickle import dumps, loads - s = dumps(PlainSentinel) - ps = loads(s) + s = pickle.dumps(PlainSentinel) + ps = pickle.loads(s) assert ps is PlainSentinel def test_custom_repr(): assert repr(SentinelCustomRepr) == '***SentinelRepr***' - + + +def test_sentinel_comes_ready_to_use(): + assert repr(Sentinel) == 'Sentinel' + s = pickle.dumps(Sentinel) + ps = pickle.loads(s) + assert ps is Sentinel From 08a4001b430f882191ac2c121082190bbb2f85c8 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sun, 23 May 2021 22:33:33 -0300 Subject: [PATCH 021/127] upgrade urllib3 due to vulnerability --- 21-futures/getflags/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/21-futures/getflags/requirements.txt b/21-futures/getflags/requirements.txt index 0971a38..447f2e9 100644 --- a/21-futures/getflags/requirements.txt +++ b/21-futures/getflags/requirements.txt @@ -5,7 +5,7 @@ certifi==2020.12.5 chardet==4.0.0 idna==2.10 requests==2.25.1 -urllib3==1.26.3 +urllib3==1.26.4 tqdm==4.56.2 multidict==5.1.0 yarl==1.6.3 From 0ce109a9fef0e1a71576268b6469aa2b02fe3a9c Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 24 May 2021 13:27:07 -0300 Subject: [PATCH 022/127] improved sentinel after learning from @taleinat on python-dev --- 25-class-metaprog/sentinel/sentinel.py | 26 +++++++++++++++++---- 25-class-metaprog/sentinel/sentinel_test.py | 21 +++++++++++++---- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/25-class-metaprog/sentinel/sentinel.py b/25-class-metaprog/sentinel/sentinel.py index 20fcc7c..1cce65e 100644 --- a/25-class-metaprog/sentinel/sentinel.py +++ b/25-class-metaprog/sentinel/sentinel.py @@ -1,23 +1,41 @@ """ +This module provides a ``Sentinel`` class that can be used directly as a +sentinel singleton, or subclassed if a distinct sentinel singleton is needed. + +The ``repr`` of a ``Sentinel`` class is its name:: >>> class Missing(Sentinel): pass >>> Missing Missing + +If a different ``repr`` is required, +you can define it as a class attribute:: + >>> class CustomRepr(Sentinel): ... repr = '' ... >>> CustomRepr +``Sentinel`` classes cannot be instantiated:: + + >>> Missing() + Traceback (most recent call last): + ... + TypeError: 'Missing' is a sentinel and cannot be instantiated + """ -class SentinelMeta(type): + +class _SentinelMeta(type): def __repr__(cls): try: return cls.repr except AttributeError: - return cls.__name__ + return f'{cls.__name__}' + -class Sentinel(metaclass=SentinelMeta): +class Sentinel(metaclass=_SentinelMeta): def __new__(cls): - return cls + msg = 'is a sentinel and cannot be instantiated' + raise TypeError(f"'{cls!r}' {msg}") diff --git a/25-class-metaprog/sentinel/sentinel_test.py b/25-class-metaprog/sentinel/sentinel_test.py index 4142289..5e304b7 100644 --- a/25-class-metaprog/sentinel/sentinel_test.py +++ b/25-class-metaprog/sentinel/sentinel_test.py @@ -1,8 +1,12 @@ import pickle +import pytest + from sentinel import Sentinel -class PlainSentinel(Sentinel): pass + +class PlainSentinel(Sentinel): + pass class SentinelCustomRepr(Sentinel): @@ -13,16 +17,23 @@ def test_repr(): assert repr(PlainSentinel) == 'PlainSentinel' -def test_pickle(): - s = pickle.dumps(PlainSentinel) - ps = pickle.loads(s) - assert ps is PlainSentinel +def test_cannot_instantiate(): + with pytest.raises(TypeError) as e: + PlainSentinel() + msg = "'PlainSentinel' is a sentinel and cannot be instantiated" + assert msg in str(e.value) def test_custom_repr(): assert repr(SentinelCustomRepr) == '***SentinelRepr***' +def test_pickle(): + s = pickle.dumps(SentinelCustomRepr) + ps = pickle.loads(s) + assert ps is SentinelCustomRepr + + def test_sentinel_comes_ready_to_use(): assert repr(Sentinel) == 'Sentinel' s = pickle.dumps(Sentinel) From e6e79b75d717038249e0988281b4a929608408e1 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 24 May 2021 13:35:53 -0300 Subject: [PATCH 023/127] removed unnecessary f-string --- 25-class-metaprog/sentinel/sentinel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/25-class-metaprog/sentinel/sentinel.py b/25-class-metaprog/sentinel/sentinel.py index 1cce65e..5cae279 100644 --- a/25-class-metaprog/sentinel/sentinel.py +++ b/25-class-metaprog/sentinel/sentinel.py @@ -32,7 +32,7 @@ def __repr__(cls): try: return cls.repr except AttributeError: - return f'{cls.__name__}' + return cls.__name__ class Sentinel(metaclass=_SentinelMeta): From 1fffea6244fb07cce222de598959a41debe49530 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 24 May 2021 13:48:55 -0300 Subject: [PATCH 024/127] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d0b57b3..7d786e1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Fluent Python 2e example code -Example code for the book **Fluent Python, 2nd edition** by Luciano Ramalho (O'Reilly, 2020). +Example code for the book **Fluent Python, 2nd edition** by Luciano Ramalho (O'Reilly, 2021). > **BEWARE**: This is a work in progress! > From abf7ac2977777764759e199fc1a1b415966299da Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 26 May 2021 19:19:24 -0300 Subject: [PATCH 025/127] ch15: typing.cast example --- 15-more-types/cast/tcp_echo.py | 34 ++++++++++++++++++++++++++ 15-more-types/cast/tcp_echo_no_cast.py | 29 ++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 15-more-types/cast/tcp_echo.py create mode 100644 15-more-types/cast/tcp_echo_no_cast.py diff --git a/15-more-types/cast/tcp_echo.py b/15-more-types/cast/tcp_echo.py new file mode 100644 index 0000000..d0f078a --- /dev/null +++ b/15-more-types/cast/tcp_echo.py @@ -0,0 +1,34 @@ +import asyncio + +from asyncio import StreamReader, StreamWriter +from asyncio.trsock import TransportSocket +from typing import cast + +async def handle_echo(reader: StreamReader, writer: StreamWriter) -> None: + data = await reader.read(100) + message = data.decode() + addr = writer.get_extra_info('peername') + + print(f"Received {message!r} from {addr!r}") + + print(f"Send: {message!r}") + writer.write(data) + await writer.drain() + + print("Close the connection") + writer.close() + +async def main() -> None: + server = await asyncio.start_server( + handle_echo, '127.0.0.1', 8888) + + # tag::CAST[] + socket_list = cast(tuple[TransportSocket, ...], server.sockets) + addr = socket_list[0].getsockname() + # end::CAST[] + print(f'Serving on {addr}') + + async with server: + await server.serve_forever() + +asyncio.run(main()) diff --git a/15-more-types/cast/tcp_echo_no_cast.py b/15-more-types/cast/tcp_echo_no_cast.py new file mode 100644 index 0000000..168db55 --- /dev/null +++ b/15-more-types/cast/tcp_echo_no_cast.py @@ -0,0 +1,29 @@ +import asyncio + +from asyncio import StreamReader, StreamWriter + +async def handle_echo(reader: StreamReader, writer: StreamWriter) -> None: + data = await reader.read(100) + message = data.decode() + addr = writer.get_extra_info('peername') + + print(f"Received {message!r} from {addr!r}") + + print(f"Send: {message!r}") + writer.write(data) + await writer.drain() + + print("Close the connection") + writer.close() + +async def main() -> None: + server = await asyncio.start_server( + handle_echo, '127.0.0.1', 8888) + + addr = server.sockets[0].getsockname() + print(f'Serving on {addr}') + + async with server: + await server.serve_forever() + +asyncio.run(main()) From 135eca25d91595e30eb8538a4b9b7f70393ad5a6 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 00:13:02 -0300 Subject: [PATCH 026/127] complete draft: update from Atlas --- 13-protocol-abc/lotto.py | 6 +- 13-protocol-abc/tombola.py | 2 +- 13-protocol-abc/tombola_runner.py | 7 +- 13-protocol-abc/tombola_tests.rst | 4 +- 13-protocol-abc/tombolist.py | 2 +- 15-more-types/cafeteria/contravariant.py | 5 +- 15-more-types/cafeteria/covariant.py | 9 +- 15-more-types/cafeteria/invariant.py | 6 +- 15-more-types/cast/empty.py | 2 + 15-more-types/cast/find.py | 23 + 15-more-types/cast/tcp_echo.py | 37 + 15-more-types/cast/tcp_echo_no_cast.py | 29 + 15-more-types/clip_annot_demo.py | 3 + .../{clip_annot_1ed.py => clip_annot_post.py} | 10 +- 15-more-types/clip_annot_signature.rst | 10 - 15-more-types/lotto/generic_lotto.py | 29 + 15-more-types/lotto/generic_lotto_demo.py | 28 + 15-more-types/lotto/generic_lotto_errors.py | 18 + 15-more-types/lotto/tombola.py | 34 + 15-more-types/mysum.py | 17 +- 15-more-types/protocol/abs_demo.py | 32 + 15-more-types/{ => protocol}/mymax/mymax.py | 4 +- .../{ => protocol}/mymax/mymax_demo.py | 0 .../{ => protocol}/mymax/mymax_test.py | 0 15-more-types/{ => protocol/random}/erp.py | 0 .../{ => protocol/random}/erp_test.py | 0 .../protocol/random/generic_randompick.py | 7 + .../random/generic_randompick_test.py} | 16 +- .../{ => protocol/random}/randompop.py | 0 .../{ => protocol/random}/randompop_test.py | 0 15-more-types/randompick_generic.py | 7 - 18-context-mngr/lispy/LICENSE | 21 + 18-context-mngr/lispy/README.md | 24 + 18-context-mngr/lispy/original/lis.py | 132 + 18-context-mngr/lispy/original/lispy.py | 316 + 18-context-mngr/lispy/original/lispytest.py | 122 + 18-context-mngr/lispy/py3.10/lis.py | 160 + 18-context-mngr/lispy/py3.10/lis_test.py | 147 + 18-context-mngr/lispy/py3.9/lis.py | 163 + 18-context-mngr/lispy/py3.9/lis_test.py | 147 + 22-async/mojifinder/tcp_mojifinder.py | 18 +- 23-dyn-attr-prop/oscon/data/osconfeed.json | 6346 ++++++++--------- .../checked/decorator/checkeddeco.py | 2 +- 43 files changed, 4705 insertions(+), 3240 deletions(-) create mode 100644 15-more-types/cast/empty.py create mode 100644 15-more-types/cast/find.py create mode 100644 15-more-types/cast/tcp_echo.py create mode 100644 15-more-types/cast/tcp_echo_no_cast.py create mode 100644 15-more-types/clip_annot_demo.py rename 15-more-types/{clip_annot_1ed.py => clip_annot_post.py} (76%) delete mode 100644 15-more-types/clip_annot_signature.rst create mode 100644 15-more-types/lotto/generic_lotto.py create mode 100755 15-more-types/lotto/generic_lotto_demo.py create mode 100755 15-more-types/lotto/generic_lotto_errors.py create mode 100644 15-more-types/lotto/tombola.py create mode 100755 15-more-types/protocol/abs_demo.py rename 15-more-types/{ => protocol}/mymax/mymax.py (91%) rename 15-more-types/{ => protocol}/mymax/mymax_demo.py (100%) rename 15-more-types/{ => protocol}/mymax/mymax_test.py (100%) rename 15-more-types/{ => protocol/random}/erp.py (100%) rename 15-more-types/{ => protocol/random}/erp_test.py (100%) create mode 100644 15-more-types/protocol/random/generic_randompick.py rename 15-more-types/{randompick_generic_test.py => protocol/random/generic_randompick_test.py} (59%) rename 15-more-types/{ => protocol/random}/randompop.py (100%) rename 15-more-types/{ => protocol/random}/randompop_test.py (100%) delete mode 100644 15-more-types/randompick_generic.py create mode 100644 18-context-mngr/lispy/LICENSE create mode 100644 18-context-mngr/lispy/README.md create mode 100644 18-context-mngr/lispy/original/lis.py create mode 100644 18-context-mngr/lispy/original/lispy.py create mode 100644 18-context-mngr/lispy/original/lispytest.py create mode 100644 18-context-mngr/lispy/py3.10/lis.py create mode 100755 18-context-mngr/lispy/py3.10/lis_test.py create mode 100644 18-context-mngr/lispy/py3.9/lis.py create mode 100755 18-context-mngr/lispy/py3.9/lis_test.py diff --git a/13-protocol-abc/lotto.py b/13-protocol-abc/lotto.py index 5026041..5fc849d 100644 --- a/13-protocol-abc/lotto.py +++ b/13-protocol-abc/lotto.py @@ -5,7 +5,7 @@ from tombola import Tombola -class LotteryBlower(Tombola): +class LottoBlower(Tombola): def __init__(self, iterable): self._balls = list(iterable) # <1> @@ -17,14 +17,14 @@ def pick(self): try: position = random.randrange(len(self._balls)) # <2> except ValueError: - raise LookupError('pick from empty BingoCage') + raise LookupError('pick from empty LottoBlower') return self._balls.pop(position) # <3> def loaded(self): # <4> return bool(self._balls) def inspect(self): # <5> - return tuple(sorted(self._balls)) + return tuple(self._balls) # end::LOTTERY_BLOWER[] diff --git a/13-protocol-abc/tombola.py b/13-protocol-abc/tombola.py index 6f4c6cd..50f19fc 100644 --- a/13-protocol-abc/tombola.py +++ b/13-protocol-abc/tombola.py @@ -28,7 +28,7 @@ def inspect(self): except LookupError: break self.load(items) # <7> - return tuple(sorted(items)) + return tuple(items) # end::TOMBOLA_ABC[] diff --git a/13-protocol-abc/tombola_runner.py b/13-protocol-abc/tombola_runner.py index bf41046..a537734 100644 --- a/13-protocol-abc/tombola_runner.py +++ b/13-protocol-abc/tombola_runner.py @@ -1,4 +1,5 @@ -# tag::TOMBOLA_RUNNER[] +#!/usr/bin/env python3 + import doctest from tombola import Tombola @@ -13,8 +14,7 @@ def main(argv): verbose = '-v' in argv real_subclasses = Tombola.__subclasses__() # <2> - virtual_subclasses = list(Tombola._abc_registry) # <3> - + virtual_subclasses = [tombolist.TomboList] # <3> for cls in real_subclasses + virtual_subclasses: # <4> test(cls, verbose) @@ -33,4 +33,3 @@ def test(cls, verbose=False): if __name__ == '__main__': import sys main(sys.argv) -# end::TOMBOLA_RUNNER[] diff --git a/13-protocol-abc/tombola_tests.rst b/13-protocol-abc/tombola_tests.rst index 1489903..6562db8 100644 --- a/13-protocol-abc/tombola_tests.rst +++ b/13-protocol-abc/tombola_tests.rst @@ -11,8 +11,8 @@ Create and load instance from iterable:: >>> globe = ConcreteTombola(balls) >>> globe.loaded() True - >>> globe.inspect() - (0, 1, 2) + >>> sorted(globe.inspect()) + [0, 1, 2] Pick and collect balls:: diff --git a/13-protocol-abc/tombolist.py b/13-protocol-abc/tombolist.py index b3ca2a6..e034e9e 100644 --- a/13-protocol-abc/tombolist.py +++ b/13-protocol-abc/tombolist.py @@ -18,6 +18,6 @@ def loaded(self): return bool(self) # <6> def inspect(self): - return tuple(sorted(self)) + return tuple(self) # Tombola.register(TomboList) # <7> diff --git a/15-more-types/cafeteria/contravariant.py b/15-more-types/cafeteria/contravariant.py index 399a748..30ef046 100644 --- a/15-more-types/cafeteria/contravariant.py +++ b/15-more-types/cafeteria/contravariant.py @@ -38,8 +38,7 @@ def deploy(trash_can: TrashCan[Biodegradable]): # tag::DEPLOY_NOT_VALID[] compost_can: TrashCan[Compostable] = TrashCan() deploy(compost_can) -# end::DEPLOY_NOT_VALID[] - -## Argument 1 to "deploy" has +## mypy: Argument 1 to "deploy" has ## incompatible type "TrashCan[Compostable]" ## expected "TrashCan[Biodegradable]" +# end::DEPLOY_NOT_VALID[] diff --git a/15-more-types/cafeteria/covariant.py b/15-more-types/cafeteria/covariant.py index 87879dd..768fe2d 100644 --- a/15-more-types/cafeteria/covariant.py +++ b/15-more-types/cafeteria/covariant.py @@ -38,11 +38,12 @@ def install(dispenser: BeverageDispenser[Juice]) -> None: # <3> install(orange_juice_dispenser) # end::INSTALL_JUICE_DISPENSERS[] -################################################ not a juice dispenser +################################################ more general dispenser +# tag::INSTALL_BEVERAGE_DISPENSER[] beverage_dispenser = BeverageDispenser(Beverage()) - -## Argument 1 to "install" has +install(beverage_dispenser) +## mypy: Argument 1 to "install" has ## incompatible type "BeverageDispenser[Beverage]" ## expected "BeverageDispenser[Juice]" -install(beverage_dispenser) +# end::INSTALL_BEVERAGE_DISPENSER[] diff --git a/15-more-types/cafeteria/invariant.py b/15-more-types/cafeteria/invariant.py index 1f9d6f6..0c44643 100644 --- a/15-more-types/cafeteria/invariant.py +++ b/15-more-types/cafeteria/invariant.py @@ -37,7 +37,7 @@ def install(dispenser: BeverageDispenser[Juice]) -> None: # <4> # tag::INSTALL_BEVERAGE_DISPENSER[] beverage_dispenser = BeverageDispenser(Beverage()) install(beverage_dispenser) -## Argument 1 to "install" has +## mypy: Argument 1 to "install" has ## incompatible type "BeverageDispenser[Beverage]" ## expected "BeverageDispenser[Juice]" # end::INSTALL_BEVERAGE_DISPENSER[] @@ -48,7 +48,7 @@ def install(dispenser: BeverageDispenser[Juice]) -> None: # <4> # tag::INSTALL_ORANGE_JUICE_DISPENSER[] orange_juice_dispenser = BeverageDispenser(OrangeJuice()) install(orange_juice_dispenser) -# end::INSTALL_ORANGE_JUICE_DISPENSER[] -## Argument 1 to "install" has +## mypy: Argument 1 to "install" has ## incompatible type "BeverageDispenser[OrangeJuice]" ## expected "BeverageDispenser[Juice]" +# end::INSTALL_ORANGE_JUICE_DISPENSER[] diff --git a/15-more-types/cast/empty.py b/15-more-types/cast/empty.py new file mode 100644 index 0000000..9e31173 --- /dev/null +++ b/15-more-types/cast/empty.py @@ -0,0 +1,2 @@ +# Mypy 0.812 can't spot this inevitable runtime IndexError +print([][0]) \ No newline at end of file diff --git a/15-more-types/cast/find.py b/15-more-types/cast/find.py new file mode 100644 index 0000000..c853ea8 --- /dev/null +++ b/15-more-types/cast/find.py @@ -0,0 +1,23 @@ +# tag::CAST[] +from typing import cast + +def find_first_str(a: list[object]) -> str: + index = next(i for i, x in enumerate(a) if isinstance(x, str)) + # We only get here if there's at least one string in a + return cast(str, a[index]) +# end::CAST[] + + +from typing import TYPE_CHECKING + +l1 = [10, 20, 'thirty', 40] +if TYPE_CHECKING: + reveal_type(l1) + +print(find_first_str(l1)) + +l2 = [0, ()] +try: + find_first_str(l2) +except StopIteration as e: + print(repr(e)) diff --git a/15-more-types/cast/tcp_echo.py b/15-more-types/cast/tcp_echo.py new file mode 100644 index 0000000..be8aaac --- /dev/null +++ b/15-more-types/cast/tcp_echo.py @@ -0,0 +1,37 @@ +import asyncio + +from asyncio import StreamReader, StreamWriter + +# tag::CAST_IMPORTS[] +from asyncio.trsock import TransportSocket +from typing import cast +# end::CAST_IMPORTS[] + +async def handle_echo(reader: StreamReader, writer: StreamWriter) -> None: + data = await reader.read(100) + message = data.decode() + addr = writer.get_extra_info('peername') + + print(f"Received {message!r} from {addr!r}") + + print(f"Send: {message!r}") + writer.write(data) + await writer.drain() + + print("Close the connection") + writer.close() + +async def main() -> None: + server = await asyncio.start_server( + handle_echo, '127.0.0.1', 8888) + + # tag::CAST_USE[] + socket_list = cast(tuple[TransportSocket, ...], server.sockets) + addr = socket_list[0].getsockname() + # end::CAST_USE[] + print(f'Serving on {addr}') + + async with server: + await server.serve_forever() + +asyncio.run(main()) diff --git a/15-more-types/cast/tcp_echo_no_cast.py b/15-more-types/cast/tcp_echo_no_cast.py new file mode 100644 index 0000000..168db55 --- /dev/null +++ b/15-more-types/cast/tcp_echo_no_cast.py @@ -0,0 +1,29 @@ +import asyncio + +from asyncio import StreamReader, StreamWriter + +async def handle_echo(reader: StreamReader, writer: StreamWriter) -> None: + data = await reader.read(100) + message = data.decode() + addr = writer.get_extra_info('peername') + + print(f"Received {message!r} from {addr!r}") + + print(f"Send: {message!r}") + writer.write(data) + await writer.drain() + + print("Close the connection") + writer.close() + +async def main() -> None: + server = await asyncio.start_server( + handle_echo, '127.0.0.1', 8888) + + addr = server.sockets[0].getsockname() + print(f'Serving on {addr}') + + async with server: + await server.serve_forever() + +asyncio.run(main()) diff --git a/15-more-types/clip_annot_demo.py b/15-more-types/clip_annot_demo.py new file mode 100644 index 0000000..dab27ae --- /dev/null +++ b/15-more-types/clip_annot_demo.py @@ -0,0 +1,3 @@ +from clip_annot_post import clip + +print(clip.__annotations__) diff --git a/15-more-types/clip_annot_1ed.py b/15-more-types/clip_annot_post.py similarity index 76% rename from 15-more-types/clip_annot_1ed.py rename to 15-more-types/clip_annot_post.py index eedd954..3760daa 100644 --- a/15-more-types/clip_annot_1ed.py +++ b/15-more-types/clip_annot_post.py @@ -1,3 +1,5 @@ +from __future__ import annotations + """ >>> clip('banana ', 6) 'banana' @@ -18,9 +20,9 @@ """ # tag::CLIP_ANNOT[] - -def clip(text: str, max_len: 'int > 0' = 80) -> str: # <1> - """Return text clipped at the last space before or after max_len +def clip(text: str, max_len: int = 80) -> str: + """Return new ``str`` clipped at last space before or after ``max_len``. + Return full ``text`` if no space found. """ end = None if len(text) > max_len: @@ -31,7 +33,7 @@ def clip(text: str, max_len: 'int > 0' = 80) -> str: # <1> space_after = text.rfind(' ', max_len) if space_after >= 0: end = space_after - if end is None: # no spaces were found + if end is None: end = len(text) return text[:end].rstrip() diff --git a/15-more-types/clip_annot_signature.rst b/15-more-types/clip_annot_signature.rst deleted file mode 100644 index 6f41aac..0000000 --- a/15-more-types/clip_annot_signature.rst +++ /dev/null @@ -1,10 +0,0 @@ ->>> from clip_annot import clip ->>> from inspect import signature ->>> sig = signature(clip) ->>> sig.return_annotation - ->>> for param in sig.parameters.values(): -... note = repr(param.annotation).ljust(13) -... print(note, ':', param.name, '=', param.default) - : text = -'int > 0' : max_len = 80 diff --git a/15-more-types/lotto/generic_lotto.py b/15-more-types/lotto/generic_lotto.py new file mode 100644 index 0000000..018d794 --- /dev/null +++ b/15-more-types/lotto/generic_lotto.py @@ -0,0 +1,29 @@ +import random + +from collections.abc import Iterable +from typing import TypeVar, Generic + +from tombola import Tombola + +T = TypeVar('T') + +class LottoBlower(Tombola, Generic[T]): # <1> + + def __init__(self, items: Iterable[T]) -> None: # <2> + self._balls = list[T](items) + + def load(self, items: Iterable[T]) -> None: # <3> + self._balls.extend(items) + + def pick(self) -> T: # <4> + try: + position = random.randrange(len(self._balls)) + except ValueError: + raise LookupError('pick from empty LottoBlower') + return self._balls.pop(position) + + def loaded(self) -> bool: # <5> + return bool(self._balls) + + def inspect(self) -> tuple[T, ...]: # <6> + return tuple(self._balls) diff --git a/15-more-types/lotto/generic_lotto_demo.py b/15-more-types/lotto/generic_lotto_demo.py new file mode 100755 index 0000000..3b63d0f --- /dev/null +++ b/15-more-types/lotto/generic_lotto_demo.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +from typing import TYPE_CHECKING + +# tag::LOTTO_USE[] +from generic_lotto import LottoBlower + +machine = LottoBlower[int](range(1, 11)) # <1> + +first = machine.pick() # <2> +remain = machine.inspect() # <3> +# end::LOTTO_USE[] + +expected = set(i for i in range(1, 11) if i != first) + +assert set(remain) == expected + +print('picked:', first) +print('remain:', remain) + +if TYPE_CHECKING: + reveal_type(first) + # Revealed type is 'builtins.int*' +if TYPE_CHECKING: + reveal_type(remain) + # Revealed type is 'builtins.tuple[builtins.int*]' + + diff --git a/15-more-types/lotto/generic_lotto_errors.py b/15-more-types/lotto/generic_lotto_errors.py new file mode 100755 index 0000000..17a34d5 --- /dev/null +++ b/15-more-types/lotto/generic_lotto_errors.py @@ -0,0 +1,18 @@ +from generic_lotto import LottoBlower + +machine = LottoBlower[int]([1, .2]) +## error: List item 1 has incompatible type "float"; # <1> +## expected "int" + +machine = LottoBlower[int](range(1, 11)) + +machine.load('ABC') +## error: Argument 1 to "load" of "LottoBlower" # <2> +## has incompatible type "str"; +## expected "Iterable[int]" +## note: Following member(s) of "str" have conflicts: +## note: Expected: +## note: def __iter__(self) -> Iterator[int] +## note: Got: +## note: def __iter__(self) -> Iterator[str] + diff --git a/15-more-types/lotto/tombola.py b/15-more-types/lotto/tombola.py new file mode 100644 index 0000000..50f19fc --- /dev/null +++ b/15-more-types/lotto/tombola.py @@ -0,0 +1,34 @@ +# tag::TOMBOLA_ABC[] + +import abc + +class Tombola(abc.ABC): # <1> + + @abc.abstractmethod + def load(self, iterable): # <2> + """Add items from an iterable.""" + + @abc.abstractmethod + def pick(self): # <3> + """Remove item at random, returning it. + + This method should raise `LookupError` when the instance is empty. + """ + + def loaded(self): # <4> + """Return `True` if there's at least 1 item, `False` otherwise.""" + return bool(self.inspect()) # <5> + + def inspect(self): + """Return a sorted tuple with the items currently inside.""" + items = [] + while True: # <6> + try: + items.append(self.pick()) + except LookupError: + break + self.load(items) # <7> + return tuple(items) + + +# end::TOMBOLA_ABC[] diff --git a/15-more-types/mysum.py b/15-more-types/mysum.py index 3fb041a..2c341aa 100644 --- a/15-more-types/mysum.py +++ b/15-more-types/mysum.py @@ -1,13 +1,14 @@ -from functools import reduce # <1> -from operator import add -from typing import overload, Iterable, Union, TypeVar +import functools +import operator +from collections.abc import Iterable +from typing import overload, Union, TypeVar T = TypeVar('T') -S = TypeVar('S') # <2> +S = TypeVar('S') # <1> @overload -def sum(it: Iterable[T]) -> Union[T, int]: ... # <3> +def sum(it: Iterable[T]) -> Union[T, int]: ... # <2> @overload -def sum(it: Iterable[T], /, start: S) -> Union[T, S]: ... # <4> -def sum(it, /, start=0): # <5> - return reduce(add, it, start) +def sum(it: Iterable[T], /, start: S) -> Union[T, S]: ... # <3> +def sum(it, /, start=0): # <4> + return functools.reduce(operator.add, it, start) diff --git a/15-more-types/protocol/abs_demo.py b/15-more-types/protocol/abs_demo.py new file mode 100755 index 0000000..9cb7f80 --- /dev/null +++ b/15-more-types/protocol/abs_demo.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 + +import math +from typing import NamedTuple, SupportsAbs + +class Vector2d(NamedTuple): + x: float + y: float + + def __abs__(self) -> float: # <1> + return math.hypot(self.x, self.y) + +def is_unit(v: SupportsAbs[float]) -> bool: # <2> + """'True' if the magnitude of 'v' is close to 1.""" + return math.isclose(abs(v), 1.0) # <3> + +assert issubclass(Vector2d, SupportsAbs) # <4> + +v0 = Vector2d(0, 1) # <5> +sqrt2 = math.sqrt(2) +v1 = Vector2d(sqrt2 / 2, sqrt2 / 2) +v2 = Vector2d(1, 1) +v3 = complex(.5, math.sqrt(3) / 2) +v4 = 1 # <6> + +assert is_unit(v0) +assert is_unit(v1) +assert not is_unit(v2) +assert is_unit(v3) +assert is_unit(v4) + +print('OK') diff --git a/15-more-types/mymax/mymax.py b/15-more-types/protocol/mymax/mymax.py similarity index 91% rename from 15-more-types/mymax/mymax.py rename to 15-more-types/protocol/mymax/mymax.py index 868ecd4..5b9f4b2 100644 --- a/15-more-types/mymax/mymax.py +++ b/15-more-types/protocol/mymax/mymax.py @@ -12,10 +12,10 @@ def __lt__(self, other: Any) -> bool: ... EMPTY_MSG = 'max() arg is an empty sequence' @overload -def max(__arg1: LT, __arg2: LT, *_args: LT, key: None = ...) -> LT: +def max(__arg1: LT, __arg2: LT, *args: LT, key: None = ...) -> LT: ... @overload -def max(__arg1: T, __arg2: T, *_args: T, key: Callable[[T], LT]) -> T: +def max(__arg1: T, __arg2: T, *args: T, key: Callable[[T], LT]) -> T: ... @overload def max(__iterable: Iterable[LT], *, key: None = ...) -> LT: diff --git a/15-more-types/mymax/mymax_demo.py b/15-more-types/protocol/mymax/mymax_demo.py similarity index 100% rename from 15-more-types/mymax/mymax_demo.py rename to 15-more-types/protocol/mymax/mymax_demo.py diff --git a/15-more-types/mymax/mymax_test.py b/15-more-types/protocol/mymax/mymax_test.py similarity index 100% rename from 15-more-types/mymax/mymax_test.py rename to 15-more-types/protocol/mymax/mymax_test.py diff --git a/15-more-types/erp.py b/15-more-types/protocol/random/erp.py similarity index 100% rename from 15-more-types/erp.py rename to 15-more-types/protocol/random/erp.py diff --git a/15-more-types/erp_test.py b/15-more-types/protocol/random/erp_test.py similarity index 100% rename from 15-more-types/erp_test.py rename to 15-more-types/protocol/random/erp_test.py diff --git a/15-more-types/protocol/random/generic_randompick.py b/15-more-types/protocol/random/generic_randompick.py new file mode 100644 index 0000000..e3ecb58 --- /dev/null +++ b/15-more-types/protocol/random/generic_randompick.py @@ -0,0 +1,7 @@ +from typing import Protocol, runtime_checkable, TypeVar + +T_co = TypeVar('T_co', covariant=True) # <1> + +@runtime_checkable +class RandomPicker(Protocol[T_co]): # <2> + def pick(self) -> T_co: ... # <3> diff --git a/15-more-types/randompick_generic_test.py b/15-more-types/protocol/random/generic_randompick_test.py similarity index 59% rename from 15-more-types/randompick_generic_test.py rename to 15-more-types/protocol/random/generic_randompick_test.py index d642b26..4e43f20 100644 --- a/15-more-types/randompick_generic_test.py +++ b/15-more-types/protocol/random/generic_randompick_test.py @@ -1,24 +1,26 @@ import random -from typing import Iterable, TYPE_CHECKING +from typing import Iterable, Generic, TypeVar, TYPE_CHECKING -from randompick_generic import GenericRandomPicker +T_co = TypeVar('T_co', covariant=True) +from generic_randompick import RandomPicker -class LottoPicker: - def __init__(self, items: Iterable[int]) -> None: + +class LottoPicker(Generic[T_co]): + def __init__(self, items: Iterable[T_co]) -> None: self._items = list(items) random.shuffle(self._items) - def pick(self) -> int: + def pick(self) -> T_co: return self._items.pop() def test_issubclass() -> None: - assert issubclass(LottoPicker, GenericRandomPicker) + assert issubclass(LottoPicker, RandomPicker) def test_isinstance() -> None: - popper: GenericRandomPicker = LottoPicker([1]) + popper: RandomPicker = LottoPicker[int]([1]) if TYPE_CHECKING: reveal_type(popper) # Revealed type is '???' diff --git a/15-more-types/randompop.py b/15-more-types/protocol/random/randompop.py similarity index 100% rename from 15-more-types/randompop.py rename to 15-more-types/protocol/random/randompop.py diff --git a/15-more-types/randompop_test.py b/15-more-types/protocol/random/randompop_test.py similarity index 100% rename from 15-more-types/randompop_test.py rename to 15-more-types/protocol/random/randompop_test.py diff --git a/15-more-types/randompick_generic.py b/15-more-types/randompick_generic.py deleted file mode 100644 index 6e4aa10..0000000 --- a/15-more-types/randompick_generic.py +++ /dev/null @@ -1,7 +0,0 @@ -from typing import Protocol, runtime_checkable, TypeVar - -T_co = TypeVar('T_co', covariant=True) - -@runtime_checkable -class GenericRandomPicker(Protocol[T_co]): - def pick(self) -> T_co: ... diff --git a/18-context-mngr/lispy/LICENSE b/18-context-mngr/lispy/LICENSE new file mode 100644 index 0000000..ca550a2 --- /dev/null +++ b/18-context-mngr/lispy/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2010-2017 Peter Norvig + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/18-context-mngr/lispy/README.md b/18-context-mngr/lispy/README.md new file mode 100644 index 0000000..1a2acfb --- /dev/null +++ b/18-context-mngr/lispy/README.md @@ -0,0 +1,24 @@ +# lis.py + +This directory contains 3 versions of +[Peter Norvig's `lis.py` interpreter](https://norvig.com/lispy.html) +for Scheme. + +* `original/`: Norvig's `lis.py` unchanged, `lispy.py` with +[minor changes](https://github.com/norvig/pytudes/pull/106) to run on Python 3, +and the `lispytest.py` custom test suite; +* `py3.9/`: `lis.py` with type hints and a few minor edits—requires Python 3.9; +* `py3.10/`: `lis.py` with type hints, pattern matching, and minor edits—requires Python 3.10. + +The `py3.9/` and `py3.10/` directories also have identical `lis_test.py` to run with +[pytest](https://docs.pytest.org). +These files include all the +[`lis_tests` suite](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/py/lispytest.py#L5) +from `original/lispytest.py`, +and individual tests for each expression and special form handled by `evaluate`. + +## Provenance, Copyright and License + +`lis.py` is published in the [norvig/pytudes](https://github.com/norvig/pytudes) repository on Github. +The copyright holder is Peter Norvig and the code is licensed under the +[MIT license](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/LICENSE). diff --git a/18-context-mngr/lispy/original/lis.py b/18-context-mngr/lispy/original/lis.py new file mode 100644 index 0000000..f81376a --- /dev/null +++ b/18-context-mngr/lispy/original/lis.py @@ -0,0 +1,132 @@ +################ Lispy: Scheme Interpreter in Python 3.3+ + +## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html + +################ Imports and Types + +import math +import operator as op +from collections import ChainMap as Environment + +Symbol = str # A Lisp Symbol is implemented as a Python str +List = list # A Lisp List is implemented as a Python list +Number = (int, float) # A Lisp Number is implemented as a Python int or float + +class Procedure(object): + "A user-defined Scheme procedure." + def __init__(self, parms, body, env): + self.parms, self.body, self.env = parms, body, env + def __call__(self, *args): + env = Environment(dict(zip(self.parms, args)), self.env) + return eval(self.body, env) + +################ Global Environment + +def standard_env(): + "An environment with some Scheme standard procedures." + env = {} + env.update(vars(math)) # sin, cos, sqrt, pi, ... + env.update({ + '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, + '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, + 'abs': abs, + 'append': op.add, + 'apply': lambda proc, args: proc(*args), + 'begin': lambda *x: x[-1], + 'car': lambda x: x[0], + 'cdr': lambda x: x[1:], + 'cons': lambda x,y: [x] + y, + 'eq?': op.is_, + 'equal?': op.eq, + 'length': len, + 'list': lambda *x: list(x), + 'list?': lambda x: isinstance(x,list), + 'map': lambda *args: list(map(*args)), + 'max': max, + 'min': min, + 'not': op.not_, + 'null?': lambda x: x == [], + 'number?': lambda x: isinstance(x, Number), + 'procedure?': callable, + 'round': round, + 'symbol?': lambda x: isinstance(x, Symbol), + }) + return env + +global_env = standard_env() + +################ Parsing: parse, tokenize, and read_from_tokens + +def parse(program): + "Read a Scheme expression from a string." + return read_from_tokens(tokenize(program)) + +def tokenize(s): + "Convert a string into a list of tokens." + return s.replace('(',' ( ').replace(')',' ) ').split() + +def read_from_tokens(tokens): + "Read an expression from a sequence of tokens." + if len(tokens) == 0: + raise SyntaxError('unexpected EOF while reading') + token = tokens.pop(0) + if '(' == token: + L = [] + while tokens[0] != ')': + L.append(read_from_tokens(tokens)) + tokens.pop(0) # pop off ')' + return L + elif ')' == token: + raise SyntaxError('unexpected )') + else: + return atom(token) + +def atom(token): + "Numbers become numbers; every other token is a symbol." + try: return int(token) + except ValueError: + try: return float(token) + except ValueError: + return Symbol(token) + +################ Interaction: A REPL + +def repl(prompt='lis.py> '): + "A prompt-read-eval-print loop." + while True: + val = eval(parse(input(prompt))) + if val is not None: + print(lispstr(val)) + +def lispstr(exp): + "Convert a Python object back into a Lisp-readable string." + if isinstance(exp, List): + return '(' + ' '.join(map(lispstr, exp)) + ')' + else: + return str(exp) + +################ eval + +def eval(x, env=global_env): + "Evaluate an expression in an environment." + if isinstance(x, Symbol): # variable reference + return env[x] + elif not isinstance(x, List): # constant literal + return x + elif x[0] == 'quote': # (quote exp) + (_, exp) = x + return exp + elif x[0] == 'if': # (if test conseq alt) + (_, test, conseq, alt) = x + exp = (conseq if eval(test, env) else alt) + return eval(exp, env) + elif x[0] == 'define': # (define var exp) + (_, var, exp) = x + env[var] = eval(exp, env) + elif x[0] == 'lambda': # (lambda (var...) body) + (_, parms, body) = x + return Procedure(parms, body, env) + else: # (proc arg...) + proc = eval(x[0], env) + args = [eval(exp, env) for exp in x[1:]] + return proc(*args) diff --git a/18-context-mngr/lispy/original/lispy.py b/18-context-mngr/lispy/original/lispy.py new file mode 100644 index 0000000..b17341c --- /dev/null +++ b/18-context-mngr/lispy/original/lispy.py @@ -0,0 +1,316 @@ +################ Scheme Interpreter in Python + +## (c) Peter Norvig, 2010; See http://norvig.com/lispy2.html + +################ Symbol, Procedure, classes + +import re, sys, io + +class Symbol(str): pass + +def Sym(s, symbol_table={}): + "Find or create unique Symbol entry for str s in symbol table." + if s not in symbol_table: symbol_table[s] = Symbol(s) + return symbol_table[s] + +_quote, _if, _set, _define, _lambda, _begin, _definemacro, = map(Sym, +"quote if set! define lambda begin define-macro".split()) + +_quasiquote, _unquote, _unquotesplicing = map(Sym, +"quasiquote unquote unquote-splicing".split()) + +class Procedure: + "A user-defined Scheme procedure." + def __init__(self, parms, exp, env): + self.parms, self.exp, self.env = parms, exp, env + def __call__(self, *args): + return eval(self.exp, Env(self.parms, args, self.env)) + +################ parse, read, and user interaction + +def parse(inport): + "Parse a program: read and expand/error-check it." + # Backwards compatibility: given a str, convert it to an InPort + if isinstance(inport, str): inport = InPort(io.StringIO(inport)) + return expand(read(inport), toplevel=True) + +eof_object = Symbol('#') # Note: uninterned; can't be read + +class InPort: + "An input port. Retains a line of chars." + tokenizer = r"""\s*(,@|[('`,)]|"(?:[\\].|[^\\"])*"|;.*|[^\s('"`,;)]*)(.*)""" + def __init__(self, file): + self.file = file; self.line = '' + def next_token(self): + "Return the next token, reading new text into line buffer if needed." + while True: + if self.line == '': self.line = self.file.readline() + if self.line == '': return eof_object + token, self.line = re.match(InPort.tokenizer, self.line).groups() + if token != '' and not token.startswith(';'): + return token + +def readchar(inport): + "Read the next character from an input port." + if inport.line != '': + ch, inport.line = inport.line[0], inport.line[1:] + return ch + else: + return inport.file.read(1) or eof_object + +def read(inport): + "Read a Scheme expression from an input port." + def read_ahead(token): + if '(' == token: + L = [] + while True: + token = inport.next_token() + if token == ')': return L + else: L.append(read_ahead(token)) + elif ')' == token: raise SyntaxError('unexpected )') + elif token in quotes: return [quotes[token], read(inport)] + elif token is eof_object: raise SyntaxError('unexpected EOF in list') + else: return atom(token) + # body of read: + token1 = inport.next_token() + return eof_object if token1 is eof_object else read_ahead(token1) + +quotes = {"'":_quote, "`":_quasiquote, ",":_unquote, ",@":_unquotesplicing} + +def atom(token): + 'Numbers become numbers; #t and #f are booleans; "..." string; otherwise Symbol.' + if token == '#t': return True + elif token == '#f': return False + elif token[0] == '"': return token[1:-1] + try: return int(token) + except ValueError: + try: return float(token) + except ValueError: + try: return complex(token.replace('i', 'j', 1)) + except ValueError: + return Sym(token) + +def to_string(x): + "Convert a Python object back into a Lisp-readable string." + if x is True: return "#t" + elif x is False: return "#f" + elif isa(x, Symbol): return x + elif isa(x, str): return repr(x) + elif isa(x, list): return '('+' '.join(map(to_string, x))+')' + elif isa(x, complex): return str(x).replace('j', 'i') + else: return str(x) + +def load(filename): + "Eval every expression from a file." + repl(None, InPort(open(filename)), None) + +def repl(prompt='lispy> ', inport=InPort(sys.stdin), out=sys.stdout): + "A prompt-read-eval-print loop." + sys.stderr.write("Lispy version 2.0\n") + while True: + try: + if prompt: sys.stderr.write(prompt) + x = parse(inport) + if x is eof_object: return + val = eval(x) + if val is not None and out: print(to_string(val), file=out) + except Exception as e: + print('%s: %s' % (type(e).__name__, e)) + +################ Environment class + +class Env(dict): + "An environment: a dict of {'var':val} pairs, with an outer Env." + def __init__(self, parms=(), args=(), outer=None): + # Bind parm list to corresponding args, or single parm to list of args + self.outer = outer + if isa(parms, Symbol): + self.update({parms:list(args)}) + else: + if len(args) != len(parms): + raise TypeError('expected %s, given %s, ' + % (to_string(parms), to_string(args))) + self.update(zip(parms,args)) + def find(self, var): + "Find the innermost Env where var appears." + if var in self: return self + elif self.outer is None: raise LookupError(var) + else: return self.outer.find(var) + +def is_pair(x): return x != [] and isa(x, list) +def cons(x, y): return [x]+y + +def callcc(proc): + "Call proc with current continuation; escape only" + ball = RuntimeWarning("Sorry, can't continue this continuation any longer.") + def throw(retval): ball.retval = retval; raise ball + try: + return proc(throw) + except RuntimeWarning as w: + if w is ball: return ball.retval + else: raise w + +def add_globals(self): + "Add some Scheme standard procedures." + import math, cmath, operator as op + self.update(vars(math)) + self.update(vars(cmath)) + self.update({ + '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, 'not':op.not_, + '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, + 'equal?':op.eq, 'eq?':op.is_, 'length':len, 'cons':cons, + 'car':lambda x:x[0], 'cdr':lambda x:x[1:], 'append':op.add, + 'list':lambda *x:list(x), 'list?': lambda x:isa(x,list), + 'null?':lambda x:x==[], 'symbol?':lambda x: isa(x, Symbol), + 'boolean?':lambda x: isa(x, bool), 'pair?':is_pair, + 'port?': lambda x:isa(x,file), 'apply':lambda proc,l: proc(*l), + 'eval':lambda x: eval(expand(x)), 'load':lambda fn: load(fn), 'call/cc':callcc, + 'open-input-file':open,'close-input-port':lambda p: p.file.close(), + 'open-output-file':lambda f:open(f,'w'), 'close-output-port':lambda p: p.close(), + 'eof-object?':lambda x:x is eof_object, 'read-char':readchar, + 'read':read, 'write':lambda x,port=sys.stdout:port.write(to_string(x)), + 'display':lambda x,port=sys.stdout:port.write(x if isa(x,str) else to_string(x))}) + return self + +isa = isinstance + +global_env = add_globals(Env()) + +################ eval (tail recursive) + +def eval(x, env=global_env): + "Evaluate an expression in an environment." + while True: + if isa(x, Symbol): # variable reference + return env.find(x)[x] + elif not isa(x, list): # constant literal + return x + elif x[0] is _quote: # (quote exp) + (_, exp) = x + return exp + elif x[0] is _if: # (if test conseq alt) + (_, test, conseq, alt) = x + x = (conseq if eval(test, env) else alt) + elif x[0] is _set: # (set! var exp) + (_, var, exp) = x + env.find(var)[var] = eval(exp, env) + return None + elif x[0] is _define: # (define var exp) + (_, var, exp) = x + env[var] = eval(exp, env) + return None + elif x[0] is _lambda: # (lambda (var*) exp) + (_, vars, exp) = x + return Procedure(vars, exp, env) + elif x[0] is _begin: # (begin exp+) + for exp in x[1:-1]: + eval(exp, env) + x = x[-1] + else: # (proc exp*) + exps = [eval(exp, env) for exp in x] + proc = exps.pop(0) + if isa(proc, Procedure): + x = proc.exp + env = Env(proc.parms, exps, proc.env) + else: + return proc(*exps) + +################ expand + +def expand(x, toplevel=False): + "Walk tree of x, making optimizations/fixes, and signaling SyntaxError." + require(x, x!=[]) # () => Error + if not isa(x, list): # constant => unchanged + return x + elif x[0] is _quote: # (quote exp) + require(x, len(x)==2) + return x + elif x[0] is _if: + if len(x)==3: x = x + [None] # (if t c) => (if t c None) + require(x, len(x)==4) + return list(map(expand, x)) + elif x[0] is _set: + require(x, len(x)==3); + var = x[1] # (set! non-var exp) => Error + require(x, isa(var, Symbol), "can set! only a symbol") + return [_set, var, expand(x[2])] + elif x[0] is _define or x[0] is _definemacro: + require(x, len(x)>=3) + _def, v, body = x[0], x[1], x[2:] + if isa(v, list) and v: # (define (f args) body) + f, args = v[0], v[1:] # => (define f (lambda (args) body)) + return expand([_def, f, [_lambda, args]+body]) + else: + require(x, len(x)==3) # (define non-var/list exp) => Error + require(x, isa(v, Symbol), "can define only a symbol") + exp = expand(x[2]) + if _def is _definemacro: + require(x, toplevel, "define-macro only allowed at top level") + proc = eval(exp) + require(x, callable(proc), "macro must be a procedure") + macro_table[v] = proc # (define-macro v proc) + return None # => None; add v:proc to macro_table + return [_define, v, exp] + elif x[0] is _begin: + if len(x)==1: return None # (begin) => None + else: return [expand(xi, toplevel) for xi in x] + elif x[0] is _lambda: # (lambda (x) e1 e2) + require(x, len(x)>=3) # => (lambda (x) (begin e1 e2)) + vars, body = x[1], x[2:] + require(x, (isa(vars, list) and all(isa(v, Symbol) for v in vars)) + or isa(vars, Symbol), "illegal lambda argument list") + exp = body[0] if len(body) == 1 else [_begin] + body + return [_lambda, vars, expand(exp)] + elif x[0] is _quasiquote: # `x => expand_quasiquote(x) + require(x, len(x)==2) + return expand_quasiquote(x[1]) + elif isa(x[0], Symbol) and x[0] in macro_table: + return expand(macro_table[x[0]](*x[1:]), toplevel) # (m arg...) + else: # => macroexpand if m isa macro + return list(map(expand, x)) # (f arg...) => expand each + +def require(x, predicate, msg="wrong length"): + "Signal a syntax error if predicate is false." + if not predicate: raise SyntaxError(to_string(x)+': '+msg) + +_append, _cons, _let = map(Sym, "append cons let".split()) + +def expand_quasiquote(x): + """Expand `x => 'x; `,x => x; `(,@x y) => (append x y) """ + if not is_pair(x): + return [_quote, x] + require(x, x[0] is not _unquotesplicing, "can't splice here") + if x[0] is _unquote: + require(x, len(x)==2) + return x[1] + elif is_pair(x[0]) and x[0][0] is _unquotesplicing: + require(x[0], len(x[0])==2) + return [_append, x[0][1], expand_quasiquote(x[1:])] + else: + return [_cons, expand_quasiquote(x[0]), expand_quasiquote(x[1:])] + +def let(*args): + args = list(args) + x = cons(_let, args) + require(x, len(args)>1) + bindings, body = args[0], args[1:] + require(x, all(isa(b, list) and len(b)==2 and isa(b[0], Symbol) + for b in bindings), "illegal binding list") + vars, vals = zip(*bindings) + return [[_lambda, list(vars)]+list(map(expand, body))] + list(map(expand, vals)) + +macro_table = {_let:let} ## More macros can go here + +eval(parse("""(begin + +(define-macro and (lambda args + (if (null? args) #t + (if (= (length args) 1) (car args) + `(if ,(car args) (and ,@(cdr args)) #f))))) + +;; More macros can also go here + +)""")) + +if __name__ == '__main__': + repl() diff --git a/18-context-mngr/lispy/original/lispytest.py b/18-context-mngr/lispy/original/lispytest.py new file mode 100644 index 0000000..b324ff3 --- /dev/null +++ b/18-context-mngr/lispy/original/lispytest.py @@ -0,0 +1,122 @@ +from __future__ import print_function + +################ Tests for lis.py and lispy.py + +lis_tests = [ + ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), + ("(+ 2 2)", 4), + ("(+ (* 2 100) (* 1 10))", 210), + ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), + ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), + ("(define x 3)", None), ("x", 3), ("(+ x x)", 6), + ("((lambda (x) (+ x x)) 5)", 10), + ("(define twice (lambda (x) (* 2 x)))", None), ("(twice 5)", 10), + ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), + ("((compose list twice) 5)", [10]), + ("(define repeat (lambda (f) (compose f f)))", None), + ("((repeat twice) 5)", 20), ("((repeat (repeat twice)) 5)", 80), + ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), + ("(fact 3)", 6), + ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), + ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), + ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), + ("""(define combine (lambda (f) + (lambda (x y) + (if (null? x) (quote ()) + (f (list (car x) (car y)) + ((combine f) (cdr x) (cdr y)))))))""", None), + ("(define zip (combine cons))", None), + ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), + ("""(define riff-shuffle (lambda (deck) (begin + (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) + (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) + (define mid (lambda (seq) (/ (length seq) 2))) + ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), + ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), + ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), + ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), + ] + +lispy_tests = [ + ("()", SyntaxError), ("(set! x)", SyntaxError), + ("(define 3 4)", SyntaxError), + ("(quote 1 2)", SyntaxError), ("(if 1 2 3 4)", SyntaxError), + ("(lambda 3 3)", SyntaxError), ("(lambda (x))", SyntaxError), + ("""(if (= 1 2) (define-macro a 'a) + (define-macro a 'b))""", SyntaxError), + ("(define (twice x) (* 2 x))", None), ("(twice 2)", 4), + ("(twice 2 2)", TypeError), + ("(define lyst (lambda items items))", None), + ("(lyst 1 2 3 (+ 2 2))", [1,2,3,4]), + ("(if 1 2)", 2), + ("(if (= 3 4) 2)", None), + ("(begin (define x 1) (set! x (+ x 1)) (+ x 1))", 3), + ("(define ((account bal) amt) (set! bal (+ bal amt)) bal)", None), + ("(define a1 (account 100))", None), + ("(a1 0)", 100), ("(a1 10)", 110), ("(a1 10)", 120), + ("""(define (newton guess function derivative epsilon) + (define guess2 (- guess (/ (function guess) (derivative guess)))) + (if (< (abs (- guess guess2)) epsilon) guess2 + (newton guess2 function derivative epsilon)))""", None), + ("""(define (square-root a) + (newton 1 (lambda (x) (- (* x x) a)) (lambda (x) (* 2 x)) 1e-8))""", None), + ("(> (square-root 200.) 14.14213)", True), + ("(< (square-root 200.) 14.14215)", True), + ("(= (square-root 200.) (sqrt 200.))", True), + ("""(define (sum-squares-range start end) + (define (sumsq-acc start end acc) + (if (> start end) acc (sumsq-acc (+ start 1) end (+ (* start start) acc)))) + (sumsq-acc start end 0))""", None), + ("(sum-squares-range 1 3000)", 9004500500), ## Tests tail recursion + ("(call/cc (lambda (throw) (+ 5 (* 10 (throw 1))))) ;; throw", 1), + ("(call/cc (lambda (throw) (+ 5 (* 10 1)))) ;; do not throw", 15), + ("""(call/cc (lambda (throw) + (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (escape 3)))))))) ; 1 level""", 35), + ("""(call/cc (lambda (throw) + (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (throw 3)))))))) ; 2 levels""", 3), + ("""(call/cc (lambda (throw) + (+ 5 (* 10 (call/cc (lambda (escape) (* 100 1))))))) ; 0 levels""", 1005), + ("(* 1i 1i)", -1), ("(sqrt -1)", 1j), + ("(let ((a 1) (b 2)) (+ a b))", 3), + ("(let ((a 1) (b 2 3)) (+ a b))", SyntaxError), + ("(and 1 2 3)", 3), ("(and (> 2 1) 2 3)", 3), ("(and)", True), + ("(and (> 2 1) (> 2 3))", False), + ("(define-macro unless (lambda args `(if (not ,(car args)) (begin ,@(cdr args))))) ; test `", None), + ("(unless (= 2 (+ 1 1)) (display 2) 3 4)", None), + (r'(unless (= 4 (+ 1 1)) (display 2) (display "\n") 3 4)', 4), + ("(quote x)", 'x'), + ("(quote (1 2 three))", [1, 2, 'three']), + ("'x", 'x'), + ("'(one 2 3)", ['one', 2, 3]), + ("(define L (list 1 2 3))", None), + ("`(testing ,@L testing)", ['testing',1,2,3,'testing']), + ("`(testing ,L testing)", ['testing',[1,2,3],'testing']), + ("`,@L", SyntaxError), + ("""'(1 ;test comments ' + ;skip this line + 2 ; more ; comments ; ) ) + 3) ; final comment""", [1,2,3]), + ] + +def test(tests, name=''): + "For each (exp, expected) test case, see if eval(parse(exp)) == expected." + fails = 0 + for (x, expected) in tests: + try: + result = eval(parse(x)) + print(x, '=>', lispstr(result)) + ok = (result == expected) + except Exception as e: + print(x, '=raises=>', type(e).__name__, e) + ok = isinstance(expected, type) and issubclass(expected, Exception) and isinstance(e, expected) + if not ok: + fails += 1 + print('FAIL!!! Expected', expected) + print('%s %s: %d out of %d tests fail.' % ('*'*45, name, fails, len(tests))) + +if __name__ == '__main__': + from lis import * + test(lis_tests, 'lis.py') + from lispy import * + test(lis_tests+lispy_tests, 'lispy.py') + diff --git a/18-context-mngr/lispy/py3.10/lis.py b/18-context-mngr/lispy/py3.10/lis.py new file mode 100644 index 0000000..ca0f8e8 --- /dev/null +++ b/18-context-mngr/lispy/py3.10/lis.py @@ -0,0 +1,160 @@ +################ Lispy: Scheme Interpreter in Python 3.9 + +## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html +## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) +## by Luciano Ramalho, adding type hints and pattern matching. + +################ Imports and Types + +import math +import operator as op +from collections import ChainMap +from collections.abc import MutableMapping +from typing import Any, TypeAlias + +Atom: TypeAlias = float | int | str +Expression: TypeAlias = Atom | list + +Environment: TypeAlias = MutableMapping[str, object] + + +class Procedure: + "A user-defined Scheme procedure." + + def __init__(self, parms: list[str], body: Expression, env: Environment): + self.parms, self.body, self.env = parms, body, env + + def __call__(self, *args: Expression) -> Any: + env: Environment = ChainMap(dict(zip(self.parms, args)), self.env) + return evaluate(self.body, env) + + +################ Global Environment + + +def standard_env() -> Environment: + "An environment with some Scheme standard procedures." + env: Environment = {} + env.update(vars(math)) # sin, cos, sqrt, pi, ... + env.update( + { + '+': op.add, + '-': op.sub, + '*': op.mul, + '/': op.truediv, + '>': op.gt, + '<': op.lt, + '>=': op.ge, + '<=': op.le, + '=': op.eq, + 'abs': abs, + 'append': op.add, + 'apply': lambda proc, args: proc(*args), + 'begin': lambda *x: x[-1], + 'car': lambda x: x[0], + 'cdr': lambda x: x[1:], + 'cons': lambda x, y: [x] + y, + 'eq?': op.is_, + 'equal?': op.eq, + 'length': len, + 'list': lambda *x: list(x), + 'list?': lambda x: isinstance(x, list), + 'map': lambda *args: list(map(*args)), + 'max': max, + 'min': min, + 'not': op.not_, + 'null?': lambda x: x == [], + 'number?': lambda x: isinstance(x, (int, float)), + 'procedure?': callable, + 'round': round, + 'symbol?': lambda x: isinstance(x, str), + } + ) + return env + + +global_env: Environment = standard_env() + +################ Parsing: parse, tokenize, and read_from_tokens + + +def parse(program: str) -> Expression: + "Read a Scheme expression from a string." + return read_from_tokens(tokenize(program)) + + +def tokenize(s: str) -> list[str]: + "Convert a string into a list of tokens." + return s.replace('(', ' ( ').replace(')', ' ) ').split() + + +def read_from_tokens(tokens: list[str]) -> Expression: + "Read an expression from a sequence of tokens." + if len(tokens) == 0: + raise SyntaxError('unexpected EOF while reading') + token = tokens.pop(0) + if '(' == token: + L = [] + while tokens[0] != ')': + L.append(read_from_tokens(tokens)) + tokens.pop(0) # pop off ')' + return L + elif ')' == token: + raise SyntaxError('unexpected )') + else: + return parse_atom(token) + + +def parse_atom(token: str) -> Atom: + "Numbers become numbers; every other token is a symbol." + try: + return int(token) + except ValueError: + try: + return float(token) + except ValueError: + return str(token) + + +################ Interaction: A REPL + + +def repl(prompt: str = 'lis.py> ') -> None: + "A prompt-read-evaluate-print loop." + while True: + val = evaluate(parse(input(prompt))) + if val is not None: + print(lispstr(val)) + + +def lispstr(exp: object) -> str: + "Convert a Python object back into a Lisp-readable string." + if isinstance(exp, list): + return '(' + ' '.join(map(lispstr, exp)) + ')' + else: + return str(exp) + + +################ eval + + +def evaluate(x: Expression, env: Environment = global_env) -> Any: + "Evaluate an expression in an environment." + match x: + case str(): # variable reference + return env[x] + case literal if not isinstance(x, list): # constant literal + return literal + case ['quote', exp]: # (quote exp) + return exp + case ['if', test, conseq, alt]: # (if test conseq alt) + exp = conseq if evaluate(test, env) else alt + return evaluate(exp, env) + case ['define', var, exp]: # (define var exp) + env[var] = evaluate(exp, env) + case ['lambda', parms, body]: # (lambda (var...) body) + return Procedure(parms, body, env) + case [op, *args]: # (proc arg...) + proc = evaluate(op, env) + values = (evaluate(arg, env) for arg in args) + return proc(*values) diff --git a/18-context-mngr/lispy/py3.10/lis_test.py b/18-context-mngr/lispy/py3.10/lis_test.py new file mode 100755 index 0000000..591046d --- /dev/null +++ b/18-context-mngr/lispy/py3.10/lis_test.py @@ -0,0 +1,147 @@ +from typing import Optional + +from pytest import mark + +from lis import parse, evaluate, Expression, Environment, standard_env + + +@mark.parametrize( 'source, expected', [ + ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), + ("(+ 2 2)", 4), + ("(+ (* 2 100) (* 1 10))", 210), + ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), + ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), + ("(define x 3)", None), ("x", 3), ("(+ x x)", 6), + ("((lambda (x) (+ x x)) 5)", 10), + ("(define twice (lambda (x) (* 2 x)))", None), ("(twice 5)", 10), + ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), + ("((compose list twice) 5)", [10]), + ("(define repeat (lambda (f) (compose f f)))", None), + ("((repeat twice) 5)", 20), ("((repeat (repeat twice)) 5)", 80), + ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), + ("(fact 3)", 6), + ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), + ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), + ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), + ("""(define combine (lambda (f) + (lambda (x y) + (if (null? x) (quote ()) + (f (list (car x) (car y)) + ((combine f) (cdr x) (cdr y)))))))""", None), + ("(define zip (combine cons))", None), + ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), + ("""(define riff-shuffle (lambda (deck) (begin + (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) + (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) + (define mid (lambda (seq) (/ (length seq) 2))) + ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), + ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), + ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), + ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), +]) +@mark.skip +def test_evaluate(source: str, expected: Optional[Expression]) -> None: + got = evaluate(parse(source)) + assert got == expected + + +# tests for each of the cases in evaluate + +def test_evaluate_variable() -> None: + env: Environment = dict(x=10) + source = 'x' + expected = 10 + got = evaluate(parse(source), env) + assert got == expected + + +def test_evaluate_literal() -> None: + source = '3.3' + expected = 3.3 + got = evaluate(parse(source)) + assert got == expected + + +def test_evaluate_quote() -> None: + source = '(quote (1.1 is not 1))' + expected = [1.1, 'is', 'not', 1] + got = evaluate(parse(source)) + assert got == expected + + +def test_evaluate_if_true() -> None: + source = '(if 1 10 no-such-thing)' + expected = 10 + got = evaluate(parse(source)) + assert got == expected + + +def test_evaluate_if_false() -> None: + source = '(if 0 no-such-thing 20)' + expected = 20 + got = evaluate(parse(source)) + assert got == expected + + +def test_define() -> None: + env: Environment = standard_env() + source = '(define answer (* 6 7))' + got = evaluate(parse(source), env) + assert got is None + assert env['answer'] == 42 + + +def test_lambda() -> None: + env: Environment = standard_env() + source = '(lambda (a b) (if (>= a b) a b))' + func = evaluate(parse(source), env) + assert func.parms == ['a', 'b'] + assert func.body == ['if', ['>=', 'a', 'b'], 'a', 'b'] + assert func.env is env + assert func(1, 2) == 2 + assert func(3, 2) == 3 + + +def test_begin() -> None: + env: Environment = standard_env() + source = """ + (begin + (define x (* 2 3)) + (* x 7) + ) + """ + got = evaluate(parse(source), env) + assert got == 42 + + +def test_invocation_builtin_car() -> None: + env: Environment = standard_env() + source = '(car (quote (11 22 33)))' + got = evaluate(parse(source), env) + assert got == 11 + + +def test_invocation_builtin_append() -> None: + env: Environment = standard_env() + source = '(append (quote (a b)) (quote (c d)))' + got = evaluate(parse(source), env) + assert got == ['a', 'b', 'c', 'd'] + + +def test_invocation_builtin_map() -> None: + env: Environment = standard_env() + source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))' + got = evaluate(parse(source), env) + assert got == [2, 4, 6] + + +def test_invocation_user_procedure() -> None: + env: Environment = standard_env() + source = """ + (begin + (define max (lambda (a b) (if (>= a b) a b))) + (max 22 11) + ) + """ + got = evaluate(parse(source), env) + assert got == 22 diff --git a/18-context-mngr/lispy/py3.9/lis.py b/18-context-mngr/lispy/py3.9/lis.py new file mode 100644 index 0000000..fcedd1e --- /dev/null +++ b/18-context-mngr/lispy/py3.9/lis.py @@ -0,0 +1,163 @@ +################ Lispy: Scheme Interpreter in Python 3.9 + +## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html +## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) +## by Luciano Ramalho, mostly adding type hints. + +################ Imports and Types + +import math +import operator as op +from collections import ChainMap +from collections.abc import MutableMapping +from typing import Union, Any + +Atom = Union[float, int, str] +Expression = Union[Atom, list] + +Environment = MutableMapping[str, object] + + +class Procedure: + "A user-defined Scheme procedure." + + def __init__(self, parms: list[str], body: Expression, env: Environment): + self.parms, self.body, self.env = parms, body, env + + def __call__(self, *args: Expression) -> Any: + env: Environment = ChainMap(dict(zip(self.parms, args)), self.env) + return evaluate(self.body, env) + + +################ Global Environment + + +def standard_env() -> Environment: + "An environment with some Scheme standard procedures." + env: Environment = {} + env.update(vars(math)) # sin, cos, sqrt, pi, ... + env.update( + { + '+': op.add, + '-': op.sub, + '*': op.mul, + '/': op.truediv, + '>': op.gt, + '<': op.lt, + '>=': op.ge, + '<=': op.le, + '=': op.eq, + 'abs': abs, + 'append': op.add, + 'apply': lambda proc, args: proc(*args), + 'begin': lambda *x: x[-1], + 'car': lambda x: x[0], + 'cdr': lambda x: x[1:], + 'cons': lambda x, y: [x] + y, + 'eq?': op.is_, + 'equal?': op.eq, + 'length': len, + 'list': lambda *x: list(x), + 'list?': lambda x: isinstance(x, list), + 'map': lambda *args: list(map(*args)), + 'max': max, + 'min': min, + 'not': op.not_, + 'null?': lambda x: x == [], + 'number?': lambda x: isinstance(x, (int, float)), + 'procedure?': callable, + 'round': round, + 'symbol?': lambda x: isinstance(x, str), + } + ) + return env + + +global_env: Environment = standard_env() + +################ Parsing: parse, tokenize, and read_from_tokens + + +def parse(program: str) -> Expression: + "Read a Scheme expression from a string." + return read_from_tokens(tokenize(program)) + + +def tokenize(s: str) -> list[str]: + "Convert a string into a list of tokens." + return s.replace('(', ' ( ').replace(')', ' ) ').split() + + +def read_from_tokens(tokens: list[str]) -> Expression: + "Read an expression from a sequence of tokens." + if len(tokens) == 0: + raise SyntaxError('unexpected EOF while reading') + token = tokens.pop(0) + if '(' == token: + L = [] + while tokens[0] != ')': + L.append(read_from_tokens(tokens)) + tokens.pop(0) # pop off ')' + return L + elif ')' == token: + raise SyntaxError('unexpected )') + else: + return parse_atom(token) + + +def parse_atom(token: str) -> Atom: + "Numbers become numbers; every other token is a symbol." + try: + return int(token) + except ValueError: + try: + return float(token) + except ValueError: + return str(token) + + +################ Interaction: A REPL + + +def repl(prompt: str = 'lis.py> ') -> None: + "A prompt-read-evaluate-print loop." + while True: + val = evaluate(parse(input(prompt))) + if val is not None: + print(lispstr(val)) + + +def lispstr(exp: object) -> str: + "Convert a Python object back into a Lisp-readable string." + if isinstance(exp, list): + return '(' + ' '.join(map(lispstr, exp)) + ')' + else: + return str(exp) + + +################ eval + + +def evaluate(x: Expression, env: Environment = global_env) -> Any: + "Evaluate an expression in an environment." + if isinstance(x, str): # variable reference + return env[x] + elif not isinstance(x, list): # constant literal + return x + elif x[0] == 'quote': # (quote exp) + (_, exp) = x + return exp + elif x[0] == 'if': # (if test conseq alt) + (_, test, conseq, alt) = x + exp = conseq if evaluate(test, env) else alt + return evaluate(exp, env) + elif x[0] == 'define': # (define var exp) + (_, var, exp) = x + env[var] = evaluate(exp, env) + elif x[0] == 'lambda': # (lambda (var...) body) + (_, parms, body) = x + return Procedure(parms, body, env) + else: # (proc arg...) + proc = evaluate(x[0], env) + args = [evaluate(exp, env) for exp in x[1:]] + return proc(*args) diff --git a/18-context-mngr/lispy/py3.9/lis_test.py b/18-context-mngr/lispy/py3.9/lis_test.py new file mode 100755 index 0000000..591046d --- /dev/null +++ b/18-context-mngr/lispy/py3.9/lis_test.py @@ -0,0 +1,147 @@ +from typing import Optional + +from pytest import mark + +from lis import parse, evaluate, Expression, Environment, standard_env + + +@mark.parametrize( 'source, expected', [ + ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), + ("(+ 2 2)", 4), + ("(+ (* 2 100) (* 1 10))", 210), + ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), + ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), + ("(define x 3)", None), ("x", 3), ("(+ x x)", 6), + ("((lambda (x) (+ x x)) 5)", 10), + ("(define twice (lambda (x) (* 2 x)))", None), ("(twice 5)", 10), + ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), + ("((compose list twice) 5)", [10]), + ("(define repeat (lambda (f) (compose f f)))", None), + ("((repeat twice) 5)", 20), ("((repeat (repeat twice)) 5)", 80), + ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), + ("(fact 3)", 6), + ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), + ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), + ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), + ("""(define combine (lambda (f) + (lambda (x y) + (if (null? x) (quote ()) + (f (list (car x) (car y)) + ((combine f) (cdr x) (cdr y)))))))""", None), + ("(define zip (combine cons))", None), + ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), + ("""(define riff-shuffle (lambda (deck) (begin + (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) + (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) + (define mid (lambda (seq) (/ (length seq) 2))) + ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), + ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), + ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), + ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), +]) +@mark.skip +def test_evaluate(source: str, expected: Optional[Expression]) -> None: + got = evaluate(parse(source)) + assert got == expected + + +# tests for each of the cases in evaluate + +def test_evaluate_variable() -> None: + env: Environment = dict(x=10) + source = 'x' + expected = 10 + got = evaluate(parse(source), env) + assert got == expected + + +def test_evaluate_literal() -> None: + source = '3.3' + expected = 3.3 + got = evaluate(parse(source)) + assert got == expected + + +def test_evaluate_quote() -> None: + source = '(quote (1.1 is not 1))' + expected = [1.1, 'is', 'not', 1] + got = evaluate(parse(source)) + assert got == expected + + +def test_evaluate_if_true() -> None: + source = '(if 1 10 no-such-thing)' + expected = 10 + got = evaluate(parse(source)) + assert got == expected + + +def test_evaluate_if_false() -> None: + source = '(if 0 no-such-thing 20)' + expected = 20 + got = evaluate(parse(source)) + assert got == expected + + +def test_define() -> None: + env: Environment = standard_env() + source = '(define answer (* 6 7))' + got = evaluate(parse(source), env) + assert got is None + assert env['answer'] == 42 + + +def test_lambda() -> None: + env: Environment = standard_env() + source = '(lambda (a b) (if (>= a b) a b))' + func = evaluate(parse(source), env) + assert func.parms == ['a', 'b'] + assert func.body == ['if', ['>=', 'a', 'b'], 'a', 'b'] + assert func.env is env + assert func(1, 2) == 2 + assert func(3, 2) == 3 + + +def test_begin() -> None: + env: Environment = standard_env() + source = """ + (begin + (define x (* 2 3)) + (* x 7) + ) + """ + got = evaluate(parse(source), env) + assert got == 42 + + +def test_invocation_builtin_car() -> None: + env: Environment = standard_env() + source = '(car (quote (11 22 33)))' + got = evaluate(parse(source), env) + assert got == 11 + + +def test_invocation_builtin_append() -> None: + env: Environment = standard_env() + source = '(append (quote (a b)) (quote (c d)))' + got = evaluate(parse(source), env) + assert got == ['a', 'b', 'c', 'd'] + + +def test_invocation_builtin_map() -> None: + env: Environment = standard_env() + source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))' + got = evaluate(parse(source), env) + assert got == [2, 4, 6] + + +def test_invocation_user_procedure() -> None: + env: Environment = standard_env() + source = """ + (begin + (define max (lambda (a b) (if (>= a b) a b))) + (max 22 11) + ) + """ + got = evaluate(parse(source), env) + assert got == 22 diff --git a/22-async/mojifinder/tcp_mojifinder.py b/22-async/mojifinder/tcp_mojifinder.py index 01e18e8..8ff4e50 100755 --- a/22-async/mojifinder/tcp_mojifinder.py +++ b/22-async/mojifinder/tcp_mojifinder.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 # tag::TCP_MOJIFINDER_TOP[] -import sys import asyncio import functools +import sys +from asyncio.trsock import TransportSocket +from typing import cast from charindex import InvertedIndex, format_results # <1> @@ -54,17 +56,19 @@ async def supervisor(index: InvertedIndex, host: str, port: int): server = await asyncio.start_server( # <1> functools.partial(finder, index), # <2> host, port) # <3> - addr = server.sockets[0].getsockname() # type: ignore # <4> - print(f'Serving on {addr}. Hit CTRL-C to stop.') - await server.serve_forever() # <5> + + socket_list = cast(tuple[TransportSocket, ...], server.sockets) # <4> + addr = socket_list[0].getsockname() + print(f'Serving on {addr}. Hit CTRL-C to stop.') # <5> + await server.serve_forever() # <6> def main(host: str = '127.0.0.1', port_arg: str = '2323'): port = int(port_arg) print('Building index.') - index = InvertedIndex() # <6> + index = InvertedIndex() # <7> try: - asyncio.run(supervisor(index, host, port)) # <7> - except KeyboardInterrupt: # <8> + asyncio.run(supervisor(index, host, port)) # <8> + except KeyboardInterrupt: # <9> print('\nServer shut down.') if __name__ == '__main__': diff --git a/23-dyn-attr-prop/oscon/data/osconfeed.json b/23-dyn-attr-prop/oscon/data/osconfeed.json index 9961768..50ed313 100644 --- a/23-dyn-attr-prop/oscon/data/osconfeed.json +++ b/23-dyn-attr-prop/oscon/data/osconfeed.json @@ -6,7 +6,7 @@ { "Schedule": { "conferences": [{"serial": 115 }], "events": [ - + { "serial": 33451, "name": "Migrating to the Web Using Dart and Polymer - A Guide for Legacy OOP Developers", @@ -16,8554 +16,8554 @@ "time_stop": "2014-07-23 17:40:00", "venue_serial": 1458, "description": "The web development platform is massive. With tons of libraries, frameworks and concepts out there, it might be daunting for the 'legacy' developer to jump into it.\r\n\r\nIn this presentation we will introduce Google Dart & Polymer. Two hot technologies that work in harmony to create powerful web applications using concepts familiar to OOP developers.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33451", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33451", "speakers": [149868], "categories": [ - + "Emerging Languages" - + ] }, - + { "serial": 33457, "name": "Refactoring 101", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1449, "description": "Refactoring code (altering code to make it cleaner, simpler, and often faster, while not sacrificing functionality) We hate to do it, so learn how to do it better. Covers: When to refactor. How to refactor. Why refactor. How refactor can help us write better code. Common methodology for refactoring.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33457", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33457", "speakers": [169862], "categories": [ - + "PHP" - + ] }, - + { "serial": 33463, "name": "Open Source and Mobile Development: Where Does it go From Here?", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1459, "description": "Until iOS and Android came along, the opportunities for open source to flourish in the mobile space were limited, because platforms were totally proprietary. Now you can find countless FL/OSS projects that help mobile developers get their job done. So what's on the horizon, and what are the best open source tools today to deliver the next great app?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33463", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33463", "speakers": [169870,2216,96208,150073], "categories": [ - + "Mobile Platforms" - + ] }, - + { "serial": 33464, "name": "Open Source Protocols and Architectures to Fix the Internet of Things", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1451, "description": "Everyday things are becoming smarter. The problem? The things are becoming smarter, but they\u2019re also becoming selfish and you\u2019ve ended up as a mechanical turk inside your own software. How can we fix the Internet of Things? The things have to become not just smarter, but more co-operative, and the Internet of things needs to become anticipatory rather than reactive.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33464", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33464", "speakers": [2216], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 33476, "name": "Scaling PHP in the Real World!", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1458, "description": "PHP is used by the likes of Facebook, Yahoo, Zynga, Tumblr, Etsy, and Wikipedia. How do the largest internet companies scale PHP to meet their demand? Join this session and find out how to use the latest tools in PHP for developing high performance applications. We\u2019ll take a look at common techniques for scaling PHP applications and best practices for profiling and optimizing performance.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33476", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33476", "speakers": [54107], "categories": [ - + "PHP" - + ] }, - + { "serial": 33481, "name": "API Ecosystem with Scala, Scalatra, and Swagger at Netflix", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1456, "description": "At Netflix Engineering's Partner Product Innovation group, we underwent a revamp of the tech stack to make it API-driven. This was to not only help with the expanding list of API consumers, but also to address the evolving streaming business. With Scala, Scalatra, and Swagger, we achieved one of the best architecture for the scale, agility and robustness needed.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33481", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33481", "speakers": [113667], "categories": [ - + "Emerging Languages" - + ] }, - + { "serial": 33485, "name": "XSS and SQL Injections: The Tip of the Web Security Iceberg ", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1458, "description": "You might know about XSS and usual SQL injection, but time has changed and we have to keep up-to-date with the latest attack scenarios.\r\nDo you also know what a clickjacking is? If not I'll show you how to protect against it.\r\nI'll also present techniques like Perfect Pixel Timing and a combination of xss/time-based-sql-injection to access intranet sites, which are not even compromised.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33485", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33485", "speakers": [169932], "categories": [ - + "PHP" - + ] }, - + { "serial": 33503, "name": "Scalable Analytics with R, Hadoop and RHadoop", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1475, "description": "Do you use Hadoop for large scale data analysis? Do your data scientists love R? This presentation will discuss the challenges of scaling R to multi-terabyte data sets and how RHadoop can be used to solve them.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33503", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33503", "speakers": [126882], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 33520, "name": "HA 101 with OpenStack", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1466, "description": "There are a number of interrelated concepts which make the understanding and implementation of HA complex. The potential for not implementing HA correctly would be disastrous. This session will use demos to reinforce the concepts and how to connect the dots using OpenStack infrastructure as an example although the lessons learned can be used for implementing HA in general.\r\n\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33520", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33520", "speakers": [131499], "categories": [ - + "Cloud" - + ] }, - + { "serial": 33549, "name": "Eyes on IZON: Surveilling IP Camera Security", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1454, "description": "This presentation will provide insight into the security mechanisms being used by the IZON IP camera, some of the weaknesses found during research, and a few recommendations for them (or anyone else developing these sorts of cameras) to benefit from. Attention will be paid to topics such as network protocols, iOS app security, APIs, and other aspects of the camera's attack surface.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33549", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33549", "speakers": [170134], "categories": [ - + "Security" - + ] }, - + { "serial": 33557, "name": "How to Fake a Database Design", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1475, "description": "Many expert programmers who write complex SQL without a second thought still struggle with database design. Unfortunately, many introductory topics cause eyes to glaze over when we read 'transitive dependencies' and 'Boyce-Codd normal form'. When you're done with this talk, you'll understand the basics of creating a database that won't make a DBA yell at you. We won't even use (many) big words.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33557", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33557", "speakers": [170158], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 33564, "name": "Testing with Test::Class::Moose", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1465, "description": "Perl is known for its testing culture. Unfortunately it's often focused on quantity over quality. Perl's Test::Class::Moose project started out as an experiment but morphed into a way of having higher quality testing. With this module, you can get fine-grained control over your test suite, better understand your *real* code coverage and get an quick boost to test suite performance.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33564", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33564", "speakers": [170158], "categories": [ - + "Perl" - + ] }, - + { "serial": 33571, "name": "An Elasticsearch Crash Course", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1475, "description": "Elasticsearch is about more than just search. It\u2019s currently being used in production for everything from traditional text search, to big data analytics, to distributed document storage. This talk will introduce you to Elasticsearch\u2019s REST API, and discuss the basics of full text search and analytics with Elasticsearch.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33571", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33571", "speakers": [170054], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 33581, "name": "Building a Massively Scalable Cloud Service from the Grounds Up", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1459, "description": "In this talk, we'll go into excruciating technical detail about building a greenfield, massively scalable cloud service. Along the path to constructing a scalable cloud service, there are many options and critical decisions to take, and we'll share our choices that brought both success and frustrations.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33581", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33581", "speakers": [116050], "categories": [ - + "Cloud" - + ] }, - + { "serial": 33585, "name": "Open/Closed Software - Approaches to Developing Freemium Applications", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1457, "description": "Developing freemium which involves OSS is not a trivial task. In this talk we\u2019ll showcase Artifactory, which successfully combines open-source and Pro versions. We will talk about developing, building, testing, and releasing hybrid freemium application and will review the existing approaches, discussing pros and cons of each of them.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33585", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33585", "speakers": [114822], "categories": [ - + "Business" - + ] }, - + { "serial": 33590, "name": "How to Build Reactive Applications", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1456, "description": "An introduction to building Reactive Applications and what tools you can use to do so.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33590", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33590", "speakers": [170293], "categories": [ - + "Java & JVM" - + ] }, - + { "serial": 33596, "name": "Designing Irresistible APIs", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1452, "description": "So you want to create a platform for your product? Creating a fantastic open API (or even a closed one) is not the same as creating other products. I'll talk about how what you need to know to design, plan and execute a successful, engaging API and how to avoid common pitfalls.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33596", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33596", "speakers": [4265], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 33597, "name": "Quantifying your Fitness", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1475, "description": "The Quantified Self movement is all about keeping measurements about your life in order to track progress in various ways. As geeks we all enjoy playing with new toys, and there are a variety of devices and applications out there to help measure steps, activity and fitness. Combining the data from these devices can help you build tools to track your fitness in a way that makes sense for you.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33597", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33597", "speakers": [4265,182808], "categories": [ - + "Geek Lifestyle" - + ] }, - + { "serial": 33627, "name": "Introduction to Docker: Containerization is the New Virtualization", "event_type": "tutorial", - + "time_start": "2014-07-20 13:30:00", "time_stop": "2014-07-20 17:00:00", "venue_serial": 1450, "description": "You've heard the hype about Docker and container virtualization now see it in action. This tutorial will introduce you to Docker and take you through installing it, running it and integrating it into your development and operational workflow.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33627", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33627", "speakers": [5060], "categories": [ - + "Operations & System Administration", - + "Tools & Techniques" - + ] }, - + { "serial": 33631, "name": "Mind the Gap: Architecting UIs in the Era of Diverse Devices", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1449, "description": "Architecting and developing user interfaces used to be relatively easy, pick a server side framework, define a standard monitor resolution and spend your days dealing with browser quirks. But today, the landscape presents us with a plethora of screen sizes and resolutions. How does a team embrace this brave new world knowing that the future will introduce even more volatility to the client space?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33631", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33631", "speakers": [108125], "categories": [ - + "User Experience" - + ] }, - + { "serial": 33648, "name": "Devoxx4Kids: So Your Kid Interested in Programming, Robotics, Engineering?", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1462, "description": "Devoxx4Kids is a worldwide initiative that introduces programming, robotics, and engineering to kids by organizing events and workshops. This session will share how Devoxx4Kids is giving Scratch, Greenfoot, Minecraft, Raspberry Pi, Arduino, NAO, Tynker workshops. The session will show a path that can be followed by parents to keep their kids engaged and build, instead of just play games. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33648", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33648", "speakers": [143377], "categories": [ - + "Education" - + ] }, - + { "serial": 33687, "name": "Debugging LAMP Apps on Linux/UNIX Using Open Source Tools", "event_type": "tutorial", - + "time_start": "2014-07-20 09:00:00", "time_stop": "2014-07-20 12:30:00", "venue_serial": 1451, "description": "The purpose of this tutorial is to train web developers working on a Linux/UNIX ENV on production, development ENVs, or both.\r\nOften, these developers, while proficient in say, PHP, lack UNIX system knowledge and therefore come across a brick wall when debugging production issues.\r\nOften times, because the development ENV is different than production.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33687", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33687", "speakers": [171078], "categories": [ - + "PHP", - + "Tools & Techniques" - + ] }, - + { "serial": 33689, "name": "Forty New Features of Java EE 7 in 40 Minutes", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1456, "description": "The Java EE 7 platform has 4 new components (WebSocket, JSON-P, batch, and concurrency), 3 that are significantly updated (JAX-RS, JMS, and EL), and several others that bring significant changes to the platform. This session explains each feature with a code snippet and provides details on where and how you can use it in your applications.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33689", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33689", "speakers": [143377], "categories": [ - + "Java & JVM" - + ] }, - + { "serial": 33704, "name": "Building a Recommendation Engine with Spring and Hadoop", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1456, "description": "Recommendation engines are the mainstay of e-commerce sites. What if you could build one with only a few lines of code using open source tools. Come to this talk to find out how as we build one using the data from StackOverflow!", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33704", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33704", "speakers": [171147], "categories": [ - + "Java & JVM" - + ] }, - + { "serial": 33709, "name": "Introduction to Parallel Iterative Deep Learning on Hadoop\u2019s Next\u200b-Generation YARN Framework", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1452, "description": "In this session, we will take a look at how we parallelize Deep Belief Networks in Deep Learning on the next\u200b-generation YARN framework Iterative Reduce and the parallel machine learning library Metronome. We\u2019ll also take a look at some real world applications of Deep Learning on Hadoop such as image classification and NLP.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33709", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33709", "speakers": [171197,171201], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 33727, "name": "Open Sourcing Mental Illness", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1451, "description": "I was diagnosed with depression and anxiety when I was thirteen, and I've been struggling with it my whole life. In this talk, I'll discuss how it has impacted my work as a developer, husband, and father. By speaking openly about my experiences, I hope those struggling with mental illness will know the are not alone, and others can better understand how to be helpful and supportive.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33727", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33727", "speakers": [1639], "categories": [ - + "Geek Lifestyle" - + ] }, - + { "serial": 33733, "name": "When PostgreSQL Can't, You Can", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1475, "description": "After using PostgreSQL for a while, you realize that there are missing features that would make it significantly easier to use in large production environments. Thankfully, it's extremely easy to make add-ons to enable some of those features right now, even without knowing C! This talk will discuss projects I've worked on and show how easy it is to make an impact in the PostgreSQL community.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33733", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33733", "speakers": [152242], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 33741, "name": "How to Achieve Enterprise Storage Functionality with OpenStack Block Storage", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1475, "description": "In this session, SolidFire's John Griffith will review some of the key features included within OpenStack Block Storage to help achieve the enterprise storage functionality they require to host production applications in their cloud infrastructure. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33741", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33741", "speakers": [171381], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 33800, "name": "Building Native iOS and Android Apps in Java", "event_type": "tutorial", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 12:30:00", "venue_serial": 1456, "description": "This tutorial will demonstrate the use of Codename One to develop a cross-platform mobile application in Java. In it you will build a non-trivial application and deploy it to your mobile device.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33800", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33800", "speakers": [171418], "categories": [ - + "Java & JVM", - + "Mobile Platforms" - + ] }, - + { "serial": 33834, "name": "Presentation Aikido", "event_type": "tutorial", - + "time_start": "2014-07-20 13:30:00", "time_stop": "2014-07-20 17:00:00", "venue_serial": 1475, "description": "This tutorial explores a set of simple and practical techniques for giving better, more effective, more entertaining technical presentations. Discover how to capture an audience, hold their interest, convey your message to them clearly\u2026and maybe even inspire them.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33834", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33834", "speakers": [4710], "categories": [ - + "Education", - + "Geek Lifestyle" - + ] }, - + { "serial": 33839, "name": "Everyday Perl 6", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1465, "description": "Perl 6's many advanced features (junctions, multiple dispatch, generics, grammars, lazy evaluation, coroutines, etc.) may well offer awesome cosmic power, but for most of us the real and immediate benefits of switching to Perl 6 are the numerous minor Perl annoyances it fixes. This talk offers a dozen practical reasons why Perl 6 might now be a better choice as your everyday go-to problem-solver.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33839", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33839", "speakers": [4710], "categories": [ - + "Perl" - + ] }, - + { "serial": 33841, "name": "The Conway Channel", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1465, "description": "Join Damian for his annual kaleidoscopic tour of the strange and wonderful new Perl modules he's been developing over the past twelve months.\r\n ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33841", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33841", "speakers": [4710], "categories": [ - + "Perl" - + ] }, - + { "serial": 33863, "name": "Building a Resilient API with Open Source", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1452, "description": "How do you build and maintain a stable API while rapidly iterating and innovating in your business? Change can never be eliminated, but its impact can be minimized. GitHub takes a pragmatic approach to Hypermedia that emphasizes workflows over data retrieval and employs open source to ensure a consistent experience for API consumers.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33863", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33863", "speakers": [109297], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 33875, "name": "The Full Stack Java Developer", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1456, "description": "Today's Java developer is a rare bird: SQL and JPA on the backend, or MongoDB or Hadoop? HTTP, REST and websockets on the web? What about security? JavaScript, HTML, CSS, (not to mention LESS, SASS, and CoffeeScript!) on the client? Today's Java developer is a _full stack_ developer. Join Josh Long and Phillip Webb for a look at how Spring Boot simplifies full-stack development for everyone.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33875", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33875", "speakers": [171564,171565], "categories": [ - + "Java & JVM", - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 33880, "name": "How Instagram.com Works", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1448, "description": "Instagram.com renders almost all of its UI in JavaScript. I'll talk about how our packaging and push systems work in great detail, which are clever combinations of existing open-source tools. Anyone building a large site using lots of JavaScript would find what we've learned interesting!", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33880", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33880", "speakers": [164756], "categories": [ - + "Main Stage" - + ] }, - + { "serial": 33913, "name": "Trolls Aren't the Only Threat Under the Bridge", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1457, "description": "There's been a lot of talk about patent trolls, but how can the free and open source software community address the more complicated (and potentially more damaging) problem of anti-competitive litigation? ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33913", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33913", "speakers": [130731], "categories": [ - + "Business", - + "Community" - + ] }, - + { "serial": 33937, "name": "Kraken.js - Bringing Open Source to the Enterprise", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1457, "description": "PayPal has recently moved their web application stack from a proprietary framework, resulting in weeks of training per developer and large maintenance costs, to an open source-based stack that allows our engineers to come in the door coding. This is the story of how we changed our enterprise culture and started giving back to the open source community.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33937", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33937", "speakers": [183835,183908], "categories": [ - + "Business" - + ] }, - + { "serial": 33943, "name": "You Don't Know How Your Computer Works", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1456, "description": "Software development is easy. You tell a computer to do something. It does it. Someone sends you a packet. The OS receives it. Things don't happen unless you ask them to. Simple.\r\n\r\nBut what if that wasn't true? What if your computer is full of hidden magic? What if your hardware makes assumptions about your software? Vendors wouldn't do that, would they?\r\n\r\n(Spoiler: Yes, they would)", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33943", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33943", "speakers": [6852], "categories": [ - + "Operations & System Administration" - + ] }, - + { "serial": 33945, "name": "Leaflet, Node.JS, and MongoDB for an Easy and Fun Web Mapping Experience", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1450, "description": "You have seen the stuff that FourSquare has done with spatial and you want some of that hotness for your app. We will load some data into MongoDB, show you how to handle spatial and finally plug in in some Node.JS JavaScript code to build simple REST services to query your data. Finally we will show how to use the REST service with OpenStreetMap and Leaflet for a fully interactive map.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33945", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33945", "speakers": [142320], "categories": [ - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 33947, "name": "The Simplicity of Clojure", "event_type": "tutorial", - + "time_start": "2014-07-20 09:00:00", "time_stop": "2014-07-20 12:30:00", "venue_serial": 1456, "description": "Clojure: it's a Lisp that runs on the JVM and it's gotten a lot of buzz in the last few years. What is it actually good for? In this tutorial, you'll learn about Clojure's radically simple approach to data and state and how it can help you build real-world projects from web applications to servers to mobile apps.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33947", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33947", "speakers": [137149,171822], "categories": [ - + "Emerging Languages", - + "Java & JVM" - + ] }, - + { "serial": 33950, "name": "There *Will* Be Bugs", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1449, "description": "If you're pushing the envelope of programming (or of your own skills)... and even when you\u2019re not... there *will* be bugs in your code. Don't panic! We cover the attitudes and skills (not taught in most schools) to minimize your bugs, track them, find them, fix them, ensure they never recur, and deploy fixes to your users.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33950", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33950", "speakers": [3471,5199], "categories": [ - + "Python" - + ] }, - + { "serial": 33973, "name": "Apache Cordova: Past, Present and Future", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1449, "description": "A review of the past six years of Apache Cordova development, starting from its origins as PhoneGap, to its donation to the Apache Software Foundation, told from the point of view of its longest running contributor. This will include a simple introduction to cross-platform hybrid applications on iOS and Android, and their evolution.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33973", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33973", "speakers": [96208], "categories": [ - + "Mobile Platforms" - + ] }, - + { "serial": 33978, "name": "WITH What? CTEs For Fun And Profit", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1466, "description": "Have you tried some recursion in your SQL? In this session, we will go over the concept of Common Table Expressions (CTE), also known as WITH queries. We will explore syntax, features, and use cases for this powerful SQL construct.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33978", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33978", "speakers": [25862], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 33994, "name": "Rebooting Open Source at Facebook", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1448, "description": "Open source has always been a huge part of Facebook's culture. But in 2013, we rebooted our portfolio and launched a unique suite of internal tools & instrumentation to support hundreds of repos, thousands of engineers, and tens of thousands of contributors. The result? Better-than-ever community adoption - and an open & responsible stewardship, attuned to our ethos of hacking & moving fast.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33994", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33994", "speakers": [118233], "categories": [ - + "Main Stage" - + ] }, - + { "serial": 33997, "name": "CERN's Approach to Mass and Agility", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1459, "description": "As part of a large-scale adoption of cloud computing to support the increasing computing needs of the Large Hadron Collider processing over 35 PB/year, the infrastructure of CERN IT is undergoing major changes in both technology and culture. This session will describe the steps taken, the challenges encountered and our outlook for the future.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33997", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33997", "speakers": [170052], "categories": [ - + "Cloud" - + ] }, - + { "serial": 34005, "name": "Let Them Be Your Heroes", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1464, "description": "How can you encourage involvement and participation in Open Source? They key is through empowerment. We'll discuss how to empower and encourage more people to participate in your open source project by enabling Heroism. This talk will also discuss issues of diversity and inclusion. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34005", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34005", "speakers": [144736], "categories": [ - + "Community" - + ] }, - + { "serial": 34012, "name": "A Presentation Toolbox that Just Might Blow Your Audience Away", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1450, "description": "Still building presentations in an office suite? That's so 2013! Today, you can build awesome, engaging presentations that run in your browser or on your phone, using nothing but HTML5 and a few clever JavaScript libraries. And it's super simple! This talk shows you how.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34012", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34012", "speakers": [131884], "categories": [ - + "Geek Lifestyle" - + ] }, - + { "serial": 34018, "name": "Raspberry Pi Hacks", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1465, "description": "Ruth Suehle, one of the authors of Raspberry Pi Hacks (O\u2019Reilly, December 2013) will offer technical tips for hardware and software hackers who want to build around the Raspberry Pi computer. She\u2019ll start with some tips like how to add a power switch and go on to share fun projects, from costume builds to radios and light displays.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34018", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34018", "speakers": [108840], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 34019, "name": "From Cloud to Fog Computing and the Internet of Things", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1449, "description": "Open Source is ubiquitous in Cloud compute. Just as we became familiar with Cloud computing, a new model has emerged, an extension of the cloud to the edge of the network, some call it Fog computing, some call it the Internet of Things. This talk describes how the compute model is changing as the new generation of devices stretched what we previously knew as Cloud compute.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34019", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34019", "speakers": [172370], "categories": [ - + "Mobile Platforms" - + ] }, - + { "serial": 34026, "name": "The Accidental DBA", "event_type": "tutorial", - + "time_start": "2014-07-21 13:30:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1475, "description": "So, you\u2019ve inherited a PostgreSQL server. Congratulations? This tutorial will cover the essential care and feeding of a Postgres server so that you can get back to your real job.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34026", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34026", "speakers": [3397], "categories": [ - + "Databases & Datastores", - + "Operations & System Administration" - + ] }, - + { "serial": 34037, "name": "Distributed Robots with Elixir", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1451, "description": "This talk will take you on a journey from making an LED blink through to building your own Elixir-powered robot using a RaspberryPi and Android.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34037", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34037", "speakers": [172532,172534], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 34040, "name": "What is Async, How Does it Work, and When Should I Use it?", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1458, "description": "Asynchronous frameworks like Tornado, Twisted, and Node are increasingly important for writing high-performance web applications. Even if you\u2019re an experienced web programmer, you may lack a rigorous understanding of how these frameworks work and when to use them. See how Tornado's event loop works, and learn how to efficiently handle very large numbers of concurrent connections.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34040", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34040", "speakers": [172536], "categories": [ - + "Python" - + ] }, - + { "serial": 34047, "name": "Go for Object Oriented Programmers (or OO Programming without Objects) ", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1465, "description": "Object Oriented programming has dominated software engineering for the last two decades. Although Go is not OO in the strict sense, we can continue to leverage the skills we've honed as OO engineers. This talk will cover how to use our OO programming fundamentals in go, common mistakes made by those coming to go from other OO languages (Ruby, Python, JS, etc.), and principles of good design in go.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34047", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34047", "speakers": [142995], "categories": [ - + "Emerging Languages" - + ] }, - + { "serial": 34056, "name": "Unicorns, Dragons, Open Source Business Models And Other Mythical Creatures", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1448, "description": "No one size fits all formula can be applied to build a business around open source, and attempting to do so may end in humiliation and disaster.\r\n\r\nThere is no doubt that 'Open Source' has impacted the dynamics of all manner of business, but building a business on 'Open Source' is not a solved problem.\r\n\r\nA guided tour of open source business models, real and imaginary.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34056", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34056", "speakers": [24052], "categories": [ - + "Main Stage" - + ] }, - + { "serial": 34063, "name": "Tutorial: Node.js Three Ways", "event_type": "tutorial", - + "time_start": "2014-07-21 13:30:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1452, "description": "In this tutorial, we\u2019ll explore three unique technologies, and accompanying use cases, for Node.js development. We\u2019ll divide the tutorial into three one-hour segments, in which you will develop three different Node.js-powered applications.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34063", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34063", "speakers": [141235,147649], "categories": [ - + "Emerging Languages", - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34064, "name": "Node.js Patterns for the Discerning Developer", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1450, "description": "In this session, I\u2019ll presenting high-quality Node.js design patterns. I\u2019ll bring to the table design patterns I\u2019ve stumbled across in my own Node projects, as well as patterns observed from experts in the Node.js community.\r\n\r\nTopics include: Mastering Modules, Object Inheritance in Node.js, Patterns to avoid callback hell, Batch and Queuing patterns for massively concurrent asynchronous I/O", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34064", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34064", "speakers": [141235], "categories": [ - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34075, "name": "Druid: Interactive Queries Meet Real-time Data", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1475, "description": "This talk will focus on the motivation, design, and architecture of Druid (druid.io), an open-source, real-time analytical data store. Druid is used in production at several organizations to facilitate rapid exploration of high dimensional spaces. Druid can maintain a 95% query latency under 1 second on data sets with >50 billion rows and 2 trillion impressions in tables with 30+ dimensions.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34075", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34075", "speakers": [172607], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 34076, "name": "Real-time Analytics with Open Source Technologies", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1475, "description": "The maturation and development of open source technologies has made it easier than ever for companies to derive insights from vast quantities of data. In this session, we will cover how to build a real-time analytics stack using Kafka, Storm, and Druid. This combination of technologies can power a robust data pipeline that supports real-time ingestion and flexible, low-latency queries.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34076", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34076", "speakers": [153565,153566], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 34078, "name": "Moose is Perl: A Guide to the New Revolution", "event_type": "tutorial", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 12:30:00", "venue_serial": 1457, "description": "Moose continues to emerge as the new standard for writing OO libraries in Perl. It provides a powerful, consistent API for building classes with a minimum of code. It can be customized with reusable components, making it easier to refactor your code as you go. This tutorial will explain what Moose is, how its parts work together, and how to start using Moose today to get more done with less.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34078", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34078", "speakers": [3189], "categories": [ - + "Perl" - + ] }, - + { "serial": 34081, "name": "Debugging HTTP", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1449, "description": "Show how tools including cURL, Wireshark and Charles can be used to inspect and change HTTP traffic when debugging applications which consume APIs and other remote sources.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34081", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34081", "speakers": [45968], "categories": [ - + "Tools & Techniques" - + ] }, - + { "serial": 34083, "name": "Creating Models", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1458, "description": "How should you organise your models in a PHP MVC application? What is a service class, a mapper or an entity? This talk will look at the components of the model layer and the options you have when creating your models. We\u2019ll look at the different schools of thought in this area and compare and contrast their strengths and weaknesses with an eye to flexibility and testability.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34083", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34083", "speakers": [46440], "categories": [ - + "PHP" - + ] }, - + { "serial": 34087, "name": "Incorporating Your Passions into Open Source Hardware ", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1451, "description": "It doesn't matter if your passion boating, fashion, kids, carpentry, architecture, race cars or rockets. Building OS hardware can be incorporated into all of these things. We will learn how to bring what you learn in your software day job into your weekend fun time. The result will be better at what you love and making work more fun. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34087", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34087", "speakers": [133377], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 34093, "name": "Tsuru: Open Source Cloud Application Platform", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1459, "description": "Tsuru is an open source, component oriented PaaS. It allows developers to focus on writing, testing and deploying applications in an easier way, without worrying how they get deployed in a server. Its key features include easy extensibility, a fully open source stack and graceful handling of failures. This talk aims to introduce Tsuru and its components, showing how they work together.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34093", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34093", "speakers": [151991], "categories": [ - + "Cloud" - + ] }, - + { "serial": 34094, "name": "Tracing and Profiling Java (and Native) Applications in Production", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1456, "description": "This talk describes the implementation and use of a full stack, low overhead tracing and profiling tool based on the Linux kernel profiler (perf) and extensions to the OpenJDK Hotspot JVM, that we've built at Twitter to help understand the behavior of the kernel, native and managed applications in production.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34094", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34094", "speakers": [172240], "categories": [ - + "Java & JVM" - + ] }, - + { "serial": 34095, "name": "Get Started Developing with Scala", "event_type": "tutorial", - + "time_start": "2014-07-20 13:30:00", "time_stop": "2014-07-20 17:00:00", "venue_serial": 1449, "description": "Scala powers some of the biggest companies in the world, including Twitter, Intel, and LinkedIn. Come learn what led them to choose this powerful JVM language and try it out yourself. You\u2019ll get a hands-on intro to Scala and functional programming concepts by building your own performant REST API. No FP experience needed--if you can build apps in Java, Python or Ruby you\u2019ll do great in this class.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34095", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34095", "speakers": [171226,150440], "categories": [ - + "Computational Thinking", - + "Java & JVM" - + ] }, - + { "serial": 34103, "name": "Reactive All The Way Down", "event_type": "tutorial", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 12:30:00", "venue_serial": 1471, "description": "In this tutorial you will build a Reactive application with Play Framework, Scala, WebSockets, and AngularJS. We will get started with a template app in Typesafe Activator. Then we will add a Reactive RESTful JSON service and a WebSocket in Scala. We will then build the UI with AngularJS.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34103", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34103", "speakers": [1158], "categories": [ - + "Java & JVM", - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34104, "name": "Building Modern Web Apps with Play Framework and Scala", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1456, "description": "Play Framework is the High Velocity Web Framework For Java and Scala. It is lightweight, stateless, RESTful, and developer friendly. This is an introduction to building web applications with Play. You will learn about: routing, Scala controllers & templates, database access, asset compilation for LESS & CoffeeScript, and JSON services.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34104", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34104", "speakers": [1158], "categories": [ - + "Java & JVM" - + ] }, - + { "serial": 34107, "name": "Full Monty: Intro to Python Metaprogramming", "event_type": "tutorial", - + "time_start": "2014-07-21 13:30:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1451, "description": "Metaprograming in Python is fun and profitable thanks to its rich Data Model \u2013 APIs that let you handle functions, modules and even classes as objects that you can create, inspect and modify at runtime. The Data Model also enables your own objects to support infix operators, become iterable and emulate collections. This workshop shows how, through a diverse selection of examples and exercises.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34107", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34107", "speakers": [150170], "categories": [ - + "Python" - + ] }, - + { "serial": 34108, "name": "Idiomatic APIs with the Python Data Model", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1465, "description": "The key to writing Pythonic classes, APIs and frameworks is leveraging the Python Data Model: a set of interfaces which, when implemented in your classes, enables them to leverage fundamental language features such as iteration, context managers, infix operators, attribute access control etc. This talk shows how, through a diverse selection of examples.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34108", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34108", "speakers": [150170], "categories": [ - + "Python" - + ] }, - + { "serial": 34109, "name": "Data.gov: Open Government as Open Source", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1454, "description": "The underpinnings of open government are transparency and citizen participation. In re-imagining a new Data.gov (the open data, open government initiative for the White House), this was taken to heart. This system was created using open source and with comments, issues, and commits worked with the public all along the way.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34109", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34109", "speakers": [62631], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 34112, "name": "Python: Encapsulation with Descriptors", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1465, "description": "Python has no private fields, but the property decorator lets you replace public attributes with getters and setters without breaking client code. And the descriptor mechanism, used in Django for model field declarations, enables wide reuse of getter/setter logic via composition instead of inheritance. This talk explains how properties and descriptors work by refactoring a practical example.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34112", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34112", "speakers": [150170], "categories": [ - + "Python" - + ] }, - + { "serial": 34116, "name": "Choosing a caching HTTP Proxy", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1450, "description": "With Web performance and scalability becoming more and more important,\r\nchoosing advanced HTTP intermediaries is a vital skill. This presentation\r\nwill give the audience a thorough walkthrough of the most popular and\r\nadvanced solutions available today. The audience will gain a solid\r\nbackground to be able to make the right choices when it comes to HTTP\r\nintermediaries and proxy caches.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34116", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34116", "speakers": [63576], "categories": [ - + "Operations & System Administration" - + ] }, - + { "serial": 34117, "name": "Git for Grown-ups", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1452, "description": "You are a clever and talented person. You have architected a system that even my cat could use; your spreadsheet-fu is legendary. Your peers adore you. Your clients love you. But, until now, you haven\u2019t *&^#^! been able to make Git work. It makes you angry inside that you have to ask for help, again, to figure out that *&^#^! command to upload your work. It's not you. It's Git. Promise.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34117", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34117", "speakers": [4146], "categories": [ - + "Tools & Techniques" - + ] }, - + { "serial": 34125, "name": "How Open Source Powers Facebook on Android", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1454, "description": "The Facebook Android app is large and developed by hundreds of software engineers. This talk will cover how OSS helps us build Facebook for Android - and how we are good OSS citizens - by looking at the full life cycle of a release, from how we organize our git repo, do code reviews in Phabricator, through building using Buck, to how we've improved the quality of our releases using Selendroid.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34125", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34125", "speakers": [172656], "categories": [ - + "Tools & Techniques" - + ] }, - + { "serial": 34135, "name": "Design for Life", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1462, "description": "It's time to design products to capture life and physiologic signs invisibly\u2026 usually through non-invasive sensors that don't require a single drop of blood, but just whiffs and sniffs. And when it is visible, it must be designed to feel wonderful.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34135", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34135", "speakers": [44753], "categories": [ - + "User Experience" - + ] }, - + { "serial": 34136, "name": "Shipping Applications to Production in Containers with Docker", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1449, "description": "Since its first release in early 2013, Docker has been deployed successfully to implement continuous integration and testing environments, where the very fast lifecycle of containers gives them an edge over virtual machines. We will see how to extend the workflow from development to testing and all the way to production. We'll also address challenges like reliability and scaling with Docker.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34136", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34136", "speakers": [151611], "categories": [ - + "Operations & System Administration" - + ] }, - + { "serial": 34137, "name": "Is it Safe to Run Applications in Linux Containers?", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1475, "description": "Linux Containers (or LXC) is now a popular choice for development and testing environments. As more and more people use them in production deployments, they face a common question: are Linux Containers secure enough? It is often claimed that containers have weaker isolation than virtual machines. We will explore whether this is true, if it matters, and what can be done about it.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34137", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34137", "speakers": [151611], "categories": [ - + "Operations & System Administration" - + ] }, - + { "serial": 34145, "name": "DevOps for University Students", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1462, "description": "University students rarely get a chance to fully embrace the Devops or FOSS development culture while in school. This year, we\u2019ve started a program called Devops Bootcamp, which is a hands-on, informal workshop open to any student at OSU. Devops Bootcamp immerses college students in the basics of Linux, Linux system administration and FOSS development practices. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34145", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34145", "speakers": [29558,123516], "categories": [ - + "Operations & System Administration" - + ] }, - + { "serial": 34148, "name": "Protocols for the Internet of Things", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1465, "description": "A large part of the internet of things will be made up of small constrained devices. The IETF is standardising protocols which are memory, energy and network efficient. Come and get an overview of these and of some Open Source implementations. See devices including Arduinos and Raspberry Pis with several sensors talking with one another and be inspired to build/connect your own devices.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34148", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34148", "speakers": [172751], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 34156, "name": "REST: It's not just for servers", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1462, "description": "Have you ever written or used an API wrapper for a webservice? REST is a client-server architecture model and building the server is only half of the challenge. This talk will walk through some of the challenges of building a REST client, describe some best practices and some patterns to avoid, and discuss how we can all work to build better APIs for an open web.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34156", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34156", "speakers": [151665], "categories": [ - + "Tools & Techniques" - + ] }, - + { "serial": 34159, "name": "Big Data Pipeline and Analytics Platform Using NetflixOSS and Other Open Source Libraries", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1452, "description": "This session presents the data platform used at Netflix for event collection, aggregation, and analysis. The platform helps Netflix process and analyze billions of events every day. Attendees will learn how to assemble their own large-scale data pipeline/analytics platform using open source software from NetflixOSS and others, such as Kafka, ElasticSearch, Druid from Metamarkets, and Hive. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34159", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34159", "speakers": [172661,171450], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 34162, "name": "Application Deployment and Auto-scaling On OpenStack using Heat ", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1466, "description": "This session will show how devops can use Heat to orchestrate the deployment &scaling of complex applications on top of OpenStack. Starting with a walk-thru of OpenStack example deployment Heat Templates for OpenShift Origin (available in openstack github repository) and enhance them to provide additional functions such as positioning alarms, responding to alarms, adding instances, &autoscaling. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34162", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34162", "speakers": [33987], "categories": [ - + "Operations & System Administration" - + ] }, - + { "serial": 34164, "name": "Timeseries Data Superpowers: Intuitive Understanding of FIR Filtering and Fourier Transforms", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1452, "description": "In this talk we'll explore the Fourier transform and FIR filters in an intuitive way to make it accessible. You'll come out with the ability to look at your time-series data in a new way and explore new uses for otherwise useless data.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34164", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34164", "speakers": [172201], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 34176, "name": "Functional Vaadin", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1466, "description": "Exploring how the functional language features of Java 8 and Scala combine with Vaadin to allow you to write clearer UI code.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34176", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34176", "speakers": [120866], "categories": [ - + "Java & JVM" - + ] }, - + { "serial": 34187, "name": "Continuous Delivery", "event_type": "tutorial", - + "time_start": "2014-07-21 13:30:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1450, "description": "Getting software released to users is often a painful, risky, and time-consuming process. This tutorial sets out the principles and technical practices that enable rapid, incremental delivery of high quality and valuable new functionality to users.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34187", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34187", "speakers": [2650], "categories": [ - + "Business", - + "Tools & Techniques" - + ] }, - + { "serial": 34188, "name": "Community War Stories : Squaring the Circle between Business and Community", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1464, "description": "In this talk we will look at some of the basic dynamics playing out in open source communities and introduce some mental models explaining them. We will look at the Open Source Flywheel (inspired by Walton's Productivity Loop and the Bezos Flywheel) and the Open Source Community Funnel (inspired by Sales Funnels) to explain them.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34188", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34188", "speakers": [62981], "categories": [ - + "Business", - + "Community" - + ] }, - + { "serial": 34192, "name": "Functional Thinking", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1452, "description": "Learning the syntax of a new language is easy, but learning to think under a different paradigm is hard. This session helps you transition from a Java writing imperative programmer to a functional programmer, using Java, Clojure and Scala for examples.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34192", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34192", "speakers": [2650], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 34194, "name": "The Curious Clojureist", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1456, "description": "Clojure is the most interesting new language on the horizon, but many developers suffer from the Blub Paradox when they see the Lisp syntax. This talk introduces Clojure to developers who haven\u2019t been exposed to it yet, focusing on the things that truly set it apart from other languages.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34194", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34194", "speakers": [2650], "categories": [ - + "Emerging Languages" - + ] }, - + { "serial": 34198, "name": "Syncing Async", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1450, "description": "'Callback hell' has very little to do with callbacks. Are promises delivering on the promise of better async flow control, or muddying the waters? Generating general generators, WAT? Let's wade through the world of async in JS to find order in the chaos.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34198", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34198", "speakers": [74368], "categories": [ - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34201, "name": "Map, Flatmap and Reduce are Your New Best Friends: Simpler Collections, Concurrency, and Big Data", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1452, "description": "Higher-order functions such as map(), flatmap(), filter() and reduce() have their origins in mathematics and ancient functional programming languages such as Lisp. But today they have become mainstream and are available in languages such as JavaScript, Scala and Java 8. Learn how to they can be used to simplify code in a variety of domains including collection processing, concurrency and big data.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34201", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34201", "speakers": [11886], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 34203, "name": "Creating Awesome Web APIs is a Breeze", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1458, "description": "Web APIs are increasingly important but their creation is still more an art than a science. This talk will demonstrate how Web APIs consumable by generic clients can be implemented in considerably less time. It will also give a brief introduction to JSON-LD and Hydra.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34203", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34203", "speakers": [172864], "categories": [ - + "PHP" - + ] }, - + { "serial": 34208, "name": "Git for Teams of One or More", "event_type": "tutorial", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 12:30:00", "venue_serial": 1452, "description": "You've dabbled a little in version control using Git. You can follow along with the various tutorials you've found online. But now you've been asked to implement a work flow strategy and you're not really sure how (or where) to start. You have a lot of choices, we'll help you pick the right one for your project.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34208", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34208", "speakers": [4146], "categories": [ - + "Community", - + "Tools & Techniques" - + ] }, - + { "serial": 34212, "name": "Telling Technology Stories with IPython Notebook", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1465, "description": "As technologists, sometimes it\u2019s as important to be able to share information with others as to be able to actually build something. IPython notebook is a powerful tool to both experiment with code (and data) and share the results with others, technical and non-technical alike. This session introduces the notebook and gives examples and techniques for using it effectively.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34212", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34212", "speakers": [59574], "categories": [ - + "Python" - + ] }, - + { "serial": 34224, "name": "Accessibility and Security - For Everyone. Gotchas to Avoid.", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1462, "description": "Did you hear about the double arm amputee who was refused service at a bank because he could not provide a thumbprint? Or the online petition to increase services for blind folks, that they couldn\u2019t sign because of CAPTCHA? These are examples of security practices that cause barriers to people with disabilities. Security can create barriers, but it doesn\u2019t have to reduce accessibility!", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34224", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34224", "speakers": [172899], "categories": [ - + "Security", - + "User Experience" - + ] }, - + { "serial": 34232, "name": "AngularJS Tutorial", "event_type": "tutorial", - + "time_start": "2014-07-20 13:30:00", "time_stop": "2014-07-20 17:00:00", "venue_serial": 1452, "description": "AngularJS is relatively new, meteorically popular, and functionally powerful. However, a lot of AngularJS\u2019s workings are very opaque and confusing. In this tutorial, my goal is to walk you through building a basic app, and introduce you to concepts, patterns, and ways of thinking that will allow you to comfortably dive further into using AngularJS for future projects.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34232", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34232", "speakers": [171372], "categories": [ - + "JavaScript - HTML5 - Web", - + "Tools & Techniques" - + ] }, - + { "serial": 34236, "name": "JavaScript and Internet Controlled Hardware Prototyping", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1451, "description": "In this session we'll be exploring how to build rapid hardware prototypes using wifi and bluetooth low energy enabled Arduino boards, all controlled through JavaScript and API data, to allow for innovative, web enabled, software to hardware development techniques.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34236", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34236", "speakers": [74565], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 34238, "name": "No More Whiteboard: Hiring Processes that Don't Waste Time", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1457, "description": "Learn how someone writes code by writing code with them. Using katas and pair programming on actual code allows you to get an honest look at the candidate's thought process and capabilities, while exposing them to your team's culture and key players. Help your evaluators be focused on what kind of people you actually want on your project by creating key prompts for them to check.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34238", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34238", "speakers": [155107], "categories": [ - + "Business" - + ] }, - + { "serial": 34243, "name": "Monitoring Your Drone Project with Elasticsearch", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1466, "description": "Elasticsearch is an open-source document store known for enabling search and real-time analytics on large data sets. In this presentation we will walk through the development of an application that monitors the Parrot AR.Drone. This application will collect metrics from the drone and then transform them to JSON for storage and real-time analysis in Elasticsearch.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34243", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34243", "speakers": [172929,142336], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 34244, "name": "Grow Your Community with Events", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1464, "description": "Events are an attractive way to grow a community, but how do you choose which types of events work best for your users? We'll cover a variety of community event types, as well as building and scaling user groups remotely, simultaneous in-person and online participation, and getting other departments at your company invested in what you're planning.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34244", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34244", "speakers": [142767], "categories": [ - + "Community" - + ] }, - + { "serial": 34247, "name": "Community Management Training", "event_type": "tutorial", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1454, "description": "This full day of community management training is delivered by Jono Bacon, author of The Art of Community, and covers a wide range of topics for community managers and leaders to build fun, productive, and rewarding communities.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34247", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34247", "speakers": [108813], "categories": [ - + "Community" - + ] }, - + { "serial": 34248, "name": "Dealing With Disrespect", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1464, "description": "In this new presentation from Jono Bacon, author of The Art of Community, founder of the Community Leadership Summit, and Ubuntu Community Manager, he discusses how to process, interpret, and manage rude, disrespectful, and non-constructive feedback in communities so the constructive criticism gets through but the hate doesn't.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34248", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34248", "speakers": [108813], "categories": [ - + "Community" - + ] }, - + { "serial": 34252, "name": "Zero to Cloud with @NetflixOSS", "event_type": "tutorial", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 12:30:00", "venue_serial": 1449, "description": "We want you to leave OSCON with a working cloud account, including supporting infrastructure that Amazon DOESN\u2019T provide but that will make your cloud life way more manageable! Once your account is bootstrapped with Asgard and Aminator, we\u2019ll be baking some of the myriad of @NetflixOSS apps. This tutorial will be meaningful for anyone getting started with or currently using AWS. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34252", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34252", "speakers": [172610], "categories": [ - + "Cloud", - + "Operations & System Administration" - + ] }, - + { "serial": 34254, "name": "Hands-On Data Analysis with Python", "event_type": "tutorial", - + "time_start": "2014-07-21 13:30:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1449, "description": "Python is quickly becoming the go-to language for data analysis. However, it can be difficult to figure out which tools are good to use. In this workshop, we\u2019ll work through in-depth examples of tools for data wrangling, machine learning, and data visualization. I\u2019ll show you how to work through a data analysis workflow, and how to deal with different kinds of data. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34254", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34254", "speakers": [170237], "categories": [ - + "Python", - + "Tools & Techniques" - + ] }, - + { "serial": 34255, "name": "Analyzing Data with Python", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1459, "description": "Python is quickly becoming the go-to language for data analysis, but it can be difficult to figure out which tools to use. In this presentation, I\u2019ll give a bird\u2019s eye overview of some of the best tools for data analysis and how you can apply them to your own workflow. I\u2019ll introduce you to how you can use Pandas, Scikit-Learn, NLTK, MRJob, and matplotlib for data analysis.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34255", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34255", "speakers": [170237], "categories": [ - + "Python" - + ] }, - + { "serial": 34258, "name": "Crash Course in Tech Management", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1452, "description": "'Programmer' and 'Manager' are two different titles for a reason: they're two different jobs and skill sets. If you have managerial aspirations (or have had them foisted upon you), come to this session to learn some of the tricks of the managerial trade.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34258", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34258", "speakers": [131404], "categories": [ - + "Business", - + "Community" - + ] }, - + { "serial": 34260, "name": "How We Went Remote", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1448, "description": "Hiring remote workers is great for filling those holes on the team...but if you don't have the correct infrastructure in place you're just setting yourself--and your team members--up for a world of hurt. This session will detail how our engineering department went remote and thrived because of it.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34260", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34260", "speakers": [131404], "categories": [ - + "Main Stage" - + ] }, - + { "serial": 34267, "name": "A Quick Introduction to System Tools Programming with Go", "event_type": "tutorial", - + "time_start": "2014-07-21 13:30:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1457, "description": "This tutorial provides an introduction to Go with a focus on using it for everyday sysadmins tooling. A example of working from iostat is used to show a practical approach to learning the language.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34267", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34267", "speakers": [172994], "categories": [ - + "Operations & System Administration", - + "Tools & Techniques" - + ] }, - + { "serial": 34273, "name": "SWI-Prolog for the Real World", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1454, "description": "Savvy functional programmers are discovering logic programming, and SWI-Prolog's vast niftiness. Come watch Annie run her debugger in reverse, directly execute syntax specifications, and lets the computer figure out it's own darn execution strategy. Be amazed as Annie constrains variables and shrinks her APIs. Ooh and Aah at the many libraries, nifty web framework and clean environment.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34273", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34273", "speakers": [172986], "categories": [ - + "Emerging Languages" - + ] }, - + { "serial": 34274, "name": "Arduino + Furby Broken Build Notification - Oh, You'll Want to Fix it Quick!", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1448, "description": "Furby's are back and more annoying than ever. Forget about a traffic light flashing or an email. When that Furby starts jabbering, you'll do ANYTHING to fix that build quickly. This talk will connect an Arduino board with Jenkins continuous integration framework and out to the Furby to let it annoy your development team, rather than you!", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34274", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34274", "speakers": [99280], "categories": [ - + "Main Stage", - + "Open Hardware" - + ] }, - + { "serial": 34275, "name": "PHP Development for Google Glass using Phass", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1458, "description": "Phass is a ZF2-based framework (implemented as a Module) designed to make building Google GlassWare applications in PHP as easy as possible. In this talk we\u2019ll show you how Phass works, complete with a live Google Glass demo of an application in action! \r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34275", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34275", "speakers": [6894], "categories": [ - + "Mobile Platforms", - + "PHP" - + ] }, - + { "serial": 34280, "name": "Portable Logic/Native UI", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1449, "description": "This talk shows how to design mobile apps whose complex internal logic runs on many mobile operating systems, but with native UI on those platforms. This ensures that the best possible user experience on each platform.\r\n\r\nThis talk focuses on design patterns for structuring your app for dealing with a mix of cross\u2013platform code and platform-specific UI code.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34280", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34280", "speakers": [109468], "categories": [ - + "Mobile Platforms" - + ] }, - + { "serial": 34281, "name": "Erlang, LFE, Joxa and Elixir: Established and Emerging Languages in the Erlang Ecosystem", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1456, "description": "Erlang is a concurrent programming language with a small, active community and many high-uptime, critical deployments. It's syntax is a bit odd, being inspired by Prolog. Other languages--Elixir, notably--have begun to reap the benefits of Erlang's VM, BEAM, modifying syntax and semantics. This talk will provide a view of the BEAM languages, their history, motivations and benefits. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34281", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34281", "speakers": [172990], "categories": [ - + "Emerging Languages" - + ] }, - + { "serial": 34283, "name": "Obey the Testing Goat! TDD for Web Development with Python", "event_type": "tutorial", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 12:30:00", "venue_serial": 1450, "description": "Learn Test-Driven-Development and how it applies to web applications by building a simple web app from scratch using Python and Django. We'll cover unit testing, Django models, views and templates, as well as using Selenium to open up a real web browser for functional tests.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34283", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34283", "speakers": [180056], "categories": [ - + "JavaScript - HTML5 - Web", - + "Python", - + "Tools & Techniques" - + ] }, - + { "serial": 34285, "name": "Idioms for Building Distributed Fault-tolerant Applications with Elixir", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1454, "description": "This talk will introduce developers to the Elixir programming language and the Erlang VM and show how they introduce a completely new vocabulary which shapes how developers design and build distributed, fault-tolerant applications. This talk also discusses Elixir goals and what it brings to the Erlang VM.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34285", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34285", "speakers": [76735], "categories": [ - + "Emerging Languages" - + ] }, - + { "serial": 34289, "name": "Lessons from Girl Develop It: Getting More Women Involved in Open Source", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1462, "description": "Women make up only 11% of open source developers. As Girl Develop It leaders in Philadelphia, we\u2019ve learned about what works to get women involved in open source projects at the grassroots level. We\u2019ll share our experience encouraging women to make open source contributions, using concrete methods that can be replicated in your own communities.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34289", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34289", "speakers": [169992,173025], "categories": [ - + "Community", - + "Education" - + ] }, - + { "serial": 34293, "name": "Performing High-Performance Parallel Data Fetching in PHP", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1458, "description": "While Node.js and other asynchronous technologies have been receiving quite a bit of attention, in this session, we'll discuss a technology stack we've written which permits PHP developers to perform complex database, cache, and API requests asynchronously, in parallel, resulting in excellent response times. This can be done natively in PHP with NO gearman and NO custom PECL extensions.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34293", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34293", "speakers": [86090], "categories": [ - + "PHP" - + ] }, - + { "serial": 34299, "name": "Painless Data Storage with MongoDB and Go", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1475, "description": "Find out why some people claim Go and MongoDB are a 'pair made in heaven' and 'the best database driver they've ever used' in this talk by Gustavo Niemeyer, the author of the mgo driver, and Steve Francia, the drivers team lead at MongoDB Inc.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34299", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34299", "speakers": [142995], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 34314, "name": "Developing Micro-services with Java and Spring", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1458, "description": "With plenty of live code and demos, this talk will show you how incredibly easy it is to write Java micro-services with modern Spring. We will walk though the process of creating a simple REST service, discuss deployment options and talk about how self-contained, stand-alone applications work in production.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34314", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34314", "speakers": [171565], "categories": [ - + "Java & JVM", - + "Tools & Techniques" - + ] }, - + { "serial": 34327, "name": "Embedding Node.js into a High-performance Network Datapath", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1450, - "description": "This talk will discuss why LineRate, a high-performance Layer 7 app proxy for developers, chose to embed Node.js as the programming language for the data path. The talk will focus on the challenges of building an embeddable\r\nNode.js and conclude with how the open source Node.js code base could evolve to better support embeddable use cases.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34327", + "description": "This talk will discuss why LineRate, a high-performance Layer 7 app proxy for developers, chose to embed Node.js as the programming language for the data path. The talk will focus on the challenges of building an embeddable\r\nNode.js and conclude with how the open source Node.js codebase could evolve to better support embeddable use cases.", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34327", "speakers": [173056], "categories": [ - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34329, "name": "Static Web Rising", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1450, "description": "A combination of open standards, open source projects, and evolving browser technologies have made static web apps an increasingly appealing target even for complex applications. Learn how you can \u201cgo static\u201d and why you might want to do so.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34329", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34329", "speakers": [2593], "categories": [ - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34332, "name": "Global Scaling at the New York Times using RabbitMQ ", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1459, "description": "Learn about the 'nyt\u2a0da\u0431rik' platform which sits behind The New York Times website. Learn how it scales across many continents and AWS availability zones using RabbitMQ as the backbone of communication for exchanging messages in near real-time. nyt\u2a0da\u0431rik is built on open-source and most of it will be open sourced.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34332", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34332", "speakers": [112672,172658], "categories": [ - + "Cloud" - + ] }, - + { "serial": 34336, "name": "One Hat, Two Hats - How to Handle Open Source and Work", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1464, "description": "Working in open source is living the dream, right? What happens when that dream clashes with the real world deliveries associated with those paying you to work in the open. Speaking from 3 years of experience working on a new tools project and through interviews with others in the industry, this talk should help show you through the ups and downs.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34336", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34336", "speakers": [39928], "categories": [ - + "Community" - + ] }, - + { "serial": 34356, "name": "Making Money at Open Source without Losing Your Soul - A Practical Guide", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1457, "description": "One of the tenets of Open Source is \u201cFree as in beer\u201d but there is still a viable commercial model. There are many successful open source companies that, despite the fact they give away their product, still manage to make money. A lot of money. How can this be possible? Let\u2019s explore some of the time tested strategies without compromising your core values.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34356", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34356", "speakers": [152376], "categories": [ - + "Business" - + ] }, - + { "serial": 34362, "name": "Money for Nothing and Your Downloads for Free", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1464, "description": "Not all projects benefit from a deep-pocketed corporate sponsor to fund their community activities. There are bills that need paying for server hosting, download bandwidth and the like, and maybe for trademark registration and other legal costs for larger projects. What's the best way to fund your project? These speakers know!", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34362", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34362", "speakers": [29591,28902,173340], "categories": [ - + "Community" - + ] }, - + { "serial": 34371, "name": "A Recovering Java Developer Learns to Go", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1461, "description": "The Go programming language has emerged as a favorite tool of DevOps and cloud practitioners alike. In many ways, Go is more famous for what it doesn't include than what it does, and co-author Rob Pike has said that Go represents a 'less is more' approach to language design. This talk will introduce Go and its distinctives to Java developers looking to add Go to their toolkits.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34371", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34371", "speakers": [173088], "categories": [ - + "Emerging Languages", - + "Java & JVM" - + ] }, - + { "serial": 34374, "name": "CSS: Declarative Nonsense Made Sensible", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1450, "description": "Unless you're working full time as a front-end engineer, odds are that CSS frustrates you from time to time. This session offers advice on how to see past the obtuse corners of CSS, backed by fifteen years' hands-on experience.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34374", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34374", "speakers": [173093], "categories": [ - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34377, "name": "NASA Open Source Projects for Science and Exploration", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1459, "description": "The Jet Propulsion Laboratory has been busy lately open sourcing its software, such as mobile apps for viewing the latest Mars images, communicating between robots, and sharing scientific analysis software in using app containers and cloud computing. Come and listen to stories and anecdotes about working on NASA projects and our journey into open source.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34377", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34377", "speakers": [173111], "categories": [ - + "Cloud" - + ] }, - + { "serial": 34378, "name": "WeIO Platform for Internet of Things", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1451, "description": "WeIO is an innovative Open Source hardware and software platform for Internet of Things that allows the creation of wirelessly connected objects using popular web languages such as HTML5 or Python.\r\nAll further details can be found on project's web-site: http://we-io.net.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34378", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34378", "speakers": [173105], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 34383, "name": "Getting Started with Scalding, Twitter's High-level Scala API for Hadoop MapReduce", "event_type": "tutorial", - + "time_start": "2014-07-21 13:30:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1471, "description": "Scalding is an open source framework developed at Twitter that provides a high level abstraction over Hadoop MapReduce, letting you concisely specify complex data analysis pipelines using simple Scala operations like map, filter, join, group, and sum. This introductory tutorial does not require experience with either Hadoop or Scala.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34383", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34383", "speakers": [90628], "categories": [ - + "Databases & Datastores", - + "Java & JVM" - + ] }, - + { "serial": 34394, "name": "Just My Type", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1465, "description": "We perl programmers aren't known as fans of formal types. Types are for straitjacketed languages like Java.\r\n\r\nBut... the Moose revolution's changing all that. Types are a great way of encapsulating the messy business of data conversion and parameter validation, and can help you think more clearly about what's going on in complex code.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34394", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34394", "speakers": [75349], "categories": [ - + "Perl" - + ] }, - + { "serial": 34395, "name": "Getting Started with Go", "event_type": "tutorial", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 12:30:00", "venue_serial": 1475, "description": "This tutorial will give developers an introduction and practical experience in building applications with the go language. Go expert Steve Francia will lead the class to build a working go web and cli application together teaching fundamentals, key features and best practices along the way. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34395", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34395", "speakers": [142995], "categories": [ - + "Emerging Languages" - + ] }, - + { "serial": 34407, "name": "Big Data Analysis 0-60 in 90 days", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1457, "description": "Do you know how long could it take to your team start producing value in the Big Data and Machine Learning area? This talk shows a real team experience starting from scratch to a functional Big Data and Machine Learning platform using several open source tools such as Apache Hadoop, Apache Hive and Python frameworks SciPy/Numpy/scikit-learn", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34407", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34407", "speakers": [173146,94695], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 34414, "name": "Train Spotting with Raspberry Pi and Data Science", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1451, "description": "Can computers tell if trains run on time? Using microphones, IP cameras, Arduino and Raspberry Pi we set up sensors to detect commuter trains as they passed by. Together with signal processing in Python, streaming data aggregation with Flume and storing in Hadoop, we\u2019ll show you how you can do this too.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34414", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34414", "speakers": [171621,133624], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 34422, "name": "Mesos: Elastically Scalable Operations, Simplified", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1466, "description": "Apache Mesos is a cluster manager that provides efficient resource isolation and sharing across distributed applications. Mesos is not only a resource scheduler but also a library for rapidly developing scalable and fault-tolerant distributed systems. This talk will take the audience through the key aspects contributing to the growing adoption of Mesos in companies with large-scale data centers.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34422", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34422", "speakers": [172898,171598], "categories": [ - + "Cloud", - + "Operations & System Administration" - + ] }, - + { "serial": 34430, "name": "How Does Raleigh Use Open Source?", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1457, "description": "Open source, open data, and open access, that's what the City of Raleigh is all about. But how did Raleigh go from open government resolution to an open data portal and a preference for open source software for IT procurement? Come to this session to learn how city government and citizens are working together to create an open source city. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34430", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34430", "speakers": [156534,173173], "categories": [ - + "Business", - + "Community" - + ] }, - + { "serial": 34431, "name": "Neo4j 2.0 Intro Training", "event_type": "tutorial", - + "time_start": "2014-07-21 13:30:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1470, "description": "This training offers the first step in building a good knowledge of graph databases, and covers the core functionality of the open source Neo4j graph database. With a mixture of theory and hands-on practice sessions, you will quickly learn how easy it is to work with a powerful graph database using Cypher as the query language. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34431", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34431", "speakers": [159719], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 34432, "name": "Mesos: An SDK for Distributed Systems Developers", "event_type": "tutorial", - + "time_start": "2014-07-20 13:30:00", "time_stop": "2014-07-20 17:00:00", "venue_serial": 1456, "description": "The shift to the cloud is old news. Unfortunately, the pain of developing distributed architectures is not. Apache Mesos handles the hard parts of building distributed systems and lets developers focus on what makes their application special. In this workshop, we will illustrate how to write applications on Mesos by walking through the implementation of an example framework.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34432", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34432", "speakers": [172973,171598,172898], "categories": [ - + "Cloud", - + "Tools & Techniques" - + ] }, - + { "serial": 34434, "name": "Hacking Lessons: Which Micro-Controller Board Do I Use?", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1451, "description": "It's a great time to be a hardware hacker. What started with the Arduino has now evolved to the Raspberry Pi, the BeagleBone Black, the Spark Core, the new Arduino Yun, and a host of other boards. How do you know which one is right for your project? This talk will compare the mainstream boards, how they are applied and help you decide which one best fits your needs. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34434", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34434", "speakers": [77350], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 34442, "name": "Perl 5.20: Perl 5 at 20", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1451, "description": "This year brings the release of Perl 5.20.0, and the 20th anniversary of the Perl 5 programming language. In this session, Ricardo Signes, the Perl 5 project manager, covers the latest developments in the language, the development process, and changes we're hoping for in the near future.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34442", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34442", "speakers": [3189], "categories": [ - + "Perl" - + ] }, - + { "serial": 34447, "name": "HTML Canvas Deep Dive", "event_type": "tutorial", - + "time_start": "2014-07-20 09:00:00", "time_stop": "2014-07-20 12:30:00", "venue_serial": 1449, "description": "In the fourth edition of this popular tutorial, we will focus on data visualization. Finding, parsing, drawing, and animating interesting data sets to promote understanding.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34447", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34447", "speakers": [6931,183609], "categories": [ - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34450, "name": "Bluetooth Low Energy: Big Progress for Small Consumption!", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1451, "description": "The last year has been great for Bluetooth LE. Supported on all smartphone OSes, hackable with Arduino and Raspberry PI, and ready for wearable computing. Review the year of BLE, and build your own smart watch.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34450", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34450", "speakers": [6931], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 34451, "name": "Netflix API : Top 10 Lessons Learned", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1449, "description": "Operating a massive-scale system, such as the Netflix API, is no trivial task. It supports over 44M members in 40+ countries and sees billions of requests a day. Along the way, there have been many mistakes, yet it is still at the center of the Netflix streaming ecosystem. In this session, I will go into detail on the top ten lessons learned in operating this complex and critical system.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34451", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34451", "speakers": [173189], "categories": [ - + "Cloud" - + ] }, - + { "serial": 34458, "name": "Start a Free Coding Club for Kids", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1462, "description": "Most Saturday mornings, Greg Bulmash brings together 70-80 boys and girls, dozens of parents and volunteers, and they teach the kids to code at a free club called CoderDojo. Come learn how to start a CoderDojo in your city and join the hundreds of cities around the world where kids are learning everything from 'hello world' to NodeCopters to building apps.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34458", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34458", "speakers": [171495], "categories": [ - + "Education" - + ] }, - + { "serial": 34459, "name": "\u201cUnfortunately, Design Tutorial Has Stopped\u201d, and Other Ways to Infuriate People With Mobile Apps", "event_type": "tutorial", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 12:30:00", "venue_serial": 1451, "description": "In this tutorial you'll learn why you can't consider UX + design an optional extra when creating mobile apps, and how to tell an awesome app from a bad app. This highly interactive platform-agnostic design-heavy workshop is for programmers of any background. Learn how mobile apps work from a UI perspective, and how + why to build wireframes, and how to evaluate your designs for future improvement.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34459", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34459", "speakers": [108884,118998,109468], "categories": [ - + "Mobile Platforms", - + "User Experience" - + ] }, - + { "serial": 34461, "name": "How Do I Game Design?", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1450, "description": "Understanding games means understanding user engagement and interaction. In this session, you'll learn a fresh perspective on user experience design by understanding how users engage with the fastest-growing form of entertainment in the world.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34461", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34461", "speakers": [108884,118998], "categories": [ - + "User Experience" - + ] }, - + { "serial": 34462, "name": "The Case for Haskell", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1462, "description": "We're building ever larger and more complex systems. Coupled with changing requirements and demands for scaling concurrency and parallelism, taming this complexity is no small order.\r\n\r\nAllow me to share my excitement with you! I'll show you how Haskell helps tame this complexity, allows you to overcome the challenges of modern software, and make predictions about what the near future holds.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34462", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34462", "speakers": [155881], "categories": [ - + "Emerging Languages" - + ] }, - + { "serial": 34463, "name": "GitGot: The Swiss Army Chainsaw of Git Repo Management", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1454, "description": "GitGot is a Perl-based tool for batch management of collections of git repos. It has a number of interesting features and acts as a force multiplier when dealing with a large varied collection of repositories. My talk will cover why you would want to use GitGot as well as how to use it effectively. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34463", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34463", "speakers": [173201], "categories": [ - + "Tools & Techniques" - + ] }, - + { "serial": 34471, "name": "My Journey as a Community Manager (Literally)", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1464, "description": "I am going to expand on the experiences of setting up a worldwide community around our product, the Neo4j graph database. I will be presenting through the prism of being a woman in the technology world and how that has affected the way i had to work.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34471", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34471", "speakers": [161517], "categories": [ - + "Community" - + ] }, - + { "serial": 34473, "name": "Get Started With the Arduino - A Hands-On Introductory Workshop", "event_type": "tutorial", - + "time_start": "2014-07-20 09:00:00", "time_stop": "2014-07-20 12:30:00", "venue_serial": 1470, "description": "Have you always wanted to create hardware devices to interact with the real world? Heard about the Arduino electronics prototyping platform but not sure how to get started? When you attend this workshop you will: set up an Arduino board & software; learn how the Arduino fits into the field of physical computing; and make your Arduino respond to button presses and blink lights. Hardware is fun!\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34473", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34473", "speakers": [77469], "categories": [ - + "Geek Lifestyle", - + "Open Hardware" - + ] }, - + { "serial": 34498, "name": "Best Practices for MySQL High Availability", "event_type": "tutorial", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 12:30:00", "venue_serial": 1470, "description": "The MySQL world is full of tradeoffs and choosing a High Availability (HA) solution is no exception. We demystify all the alternatives in an unbiased nature. Preference is of course only given to opensource solutions. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34498", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34498", "speakers": [147], "categories": [ - + "Databases & Datastores", - + "Operations & System Administration" - + ] }, - + { "serial": 34505, "name": "Why Schools Don't Use Open Source to Teach Programming", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1462, "description": "Aside from the fact that high school programming curricula often require proprietary IDEs, they also don't involve examining any source code from Open Source software projects. What changes would be required in programming curricula to incorporate Open Source? And is that a desirable objective?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34505", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34505", "speakers": [157509], "categories": [ - + "Education" - + ] }, - + { "serial": 34506, "name": "How to Deploy PHP Apps Safely, Efficiently, and Frequently without Losing Your Sanity (Completely)", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1458, "description": "Sane and safe continuous deployment (and testing) can be achieved without much effort using a set of freely-available open-source tools, such as a good source control system, Phing, PHPUnit, some security tools, phpDocumentor and others.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34506", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34506", "speakers": [173235], "categories": [ - + "PHP" - + ] }, - + { "serial": 34509, "name": "Inside the Go Tour", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1458, "description": "One of the most important tools created to help people learn Go is the Go tour (http://tour.golang.org)\r\n\r\nIt allows the user to learn the basics of Go and put them in practice directly on their browsers, running code without installing any compiler.\r\n\r\nImplementing this in a safe way is not an easy task! In this talk I present some techniques used to make sure everything goes as expected.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34509", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34509", "speakers": [155088], "categories": [ - + "Security" - + ] }, - + { "serial": 34522, "name": "Internet ALL the Things - a walking tour of MQTT", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1465, "description": "As the internet grows, there are more and more interesting devices to connect to it - some of which are mobile, sensor platforms, or healthcare devices. This is all part of the 'Internet of Things' that has been an emerging area of excitement for the last few years. MQTT is a lightweight, messaging system for connected devices, the Industrial Internet, mobile, and the IoT.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34522", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34522", "speakers": [141661], "categories": [ - + "Mobile Platforms" - + ] }, - + { "serial": 34530, "name": "Thinking in a Highly Concurrent, Mostly-functional Language", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1452, "description": "The actor model has received much attention because of its scalable and intuitive approach to concurrency. But the notion of concurrency is as fundamental to certain languages as object-orientation is to Java. In this talk, we will describe the evolution of concurrent thinking in Erlang, providing valuable lessons for Go, Rust, Elixir and AKKA developers who will undertake a similar journey.. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34530", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34530", "speakers": [10595], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 34535, "name": "Cheap Data Dashboards with Node, Amino and the Raspberry PI", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1458, "description": "Cheap LCD TV + Raspberry Pi = instant data dashboard. Learn how to use NodeJS and Amino for full screen GPU accelerated graphics (no X) to quickly build data dashboards. Show feeds, chart tweets, or visualize your build server with a particle fountain. Unleash gratuitous graphics for all to see.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34535", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34535", "speakers": [6931], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 34536, "name": "HTML5 JavaScript Storage for Structured Data", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1450, "description": "Learn about going beyond simple cookies and busting the 5MB limit imposed by Web Storage. We'll dive into the IndexedDB API and open your world to reading and writing not just strings from within browser storage, but also blobs, Arrays and Objects too.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34536", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34536", "speakers": [2397], "categories": [ - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34542, "name": "Multiple Datastores Working Together: Will It Blend?", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1475, "description": "There has been an explosion in datastore technologies. There are five main types of datastores: Relational, Column Family, Graph, Key-Value and Document. Polyglot Persistence, or the ability to have many different types of datastores interacting with one application, is becoming more prominent and beginning to take center stage.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34542", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34542", "speakers": [159586], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 34551, "name": "Marketing Your Tech Talent ", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1454, "description": "Today's tech job descriptions want 'superstars', but most companies \u2013 and employees! \u2013 still treat employee talent as a replaceable commodity. How can you market yourself and your talents, to benefit your own career as well as the company or project you work for? This talk will provide practical ideas and real-life case studies, based on years of experience helping geeks communicate what they do.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34551", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34551", "speakers": [122293], "categories": [ - + "Geek Lifestyle" - + ] }, - + { "serial": 34552, "name": "Improv: Think, React, Go!", "event_type": "tutorial", - + "time_start": "2014-07-21 13:30:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1456, "description": "Getting everyone in your company or development team on the same page can be a challenge. This on-your-feet workshop will teach fast, fun improv techniques for helping your group to bond, generate quality ideas and make quick decisions. Learn the secrets of applied improv from two professionals who have decades of experience working in open source, Internet startups and corporate training.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34552", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34552", "speakers": [4378,106355,123894], "categories": [ - + "Business", - + "Community" - + ] }, - + { "serial": 34555, "name": "Build your Own Android App using Open Source Libraries - A Hands On Tutorial", "event_type": "tutorial", - + "time_start": "2014-07-20 13:30:00", "time_stop": "2014-07-20 17:00:00", "venue_serial": 1471, "description": "In this tutorial, we will develop a working Android application using open source libraries for key platform components: HTTP client, JSON parsing, Async image download and caching.\r\n\r\nYou will learn how to manage dependencies using Gradle and best practices for building Android apps using open source libraries.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34555", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34555", "speakers": [173281,182019], "categories": [ - + "Mobile Platforms" - + ] }, - + { "serial": 34568, "name": "React's Architecture", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1449, "description": "React is a JavaScript library for building user interfaces developed by Facebook and Instagram. It has a novel rendering architecture that we're going to explore in this talk.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34568", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34568", "speakers": [133198], "categories": [ - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34570, "name": "Satisfying Business and Engineering Requirements: Client-server JavaScript, SEO, and Optimized Page Load", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1457, "description": "Often business and developer needs are at odds when developing public facing websites that need to be indexed. Business is concerned with factors such as SEO, visitor retention and bounce rates, while engineering is concerned with developer ergonomics, re-usage, separation of concerns, and maintenance. This talk will describe a solution that satisfies both business and engineering requirements.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34570", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34570", "speakers": [135908], "categories": [ - + "Business", - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34575, "name": "Open-Source DoS Testing and Defense", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1466, "description": "Denial of Service (DoS) attacks have been making the news lately -- can your site hold up? In this talk, we'll look at a number of open-source tools for testing your site and walk through ways to guard yourself against web attackers.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34575", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34575", "speakers": [173285], "categories": [ - + "Security" - + ] }, - + { "serial": 34578, "name": "ETL: The Dirty Little Secret of Data Science", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1475, "description": "There is an adage that given enough data, a data scientist can answer the world's questions. The untold truth is that the majority of work happens during the ETL and data preprocessing phase. In this talk I discuss Origins, an open source Python library for extracting and mapping structural metadata across heterogenous data stores.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34578", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34578", "speakers": [152026], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 34587, "name": "Monitoring Distributed Systems in Real-time with Riemann and Cassandra", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1449, "description": "Computing is spreading outwards: clusters of 1000s of nodes serve a single database, and hundreds of machines analyze the same KPIs.\r\n\r\nHow do we monitor a cluster with many nodes?\r\n\r\nThis talk presents how to effectively monitor a multi-node Cassandra cluster using Riemann and other graphing solutions.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34587", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34587", "speakers": [156989], "categories": [ - + "Operations & System Administration" - + ] }, - + { "serial": 34588, "name": "Getting Started Contributing to Firefox OS", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1466, "description": "Firefox OS is a new mobile operating system, developed by Mozilla, which lets users install and run open web applications created using HTML, CSS, and JavaScript.\r\nThe session will introduce people to Firefox OS, the overview, branding and distribution and will explain the governance behind it. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34588", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34588", "speakers": [120025,173308], "categories": [ - + "Mobile Platforms" - + ] }, - + { "serial": 34589, "name": "Make your Open Source More Open \u2013 Conquering the Accessibility Challenge", "event_type": "tutorial", - + "time_start": "2014-07-20 09:00:00", "time_stop": "2014-07-20 12:30:00", "venue_serial": 1457, "description": "How accessible are your development projects? This session puts development to the ultimate accessibility test. The presenters will guide you through an experience of accessibility for people who are blind and then go on to cover best practices, testing, and pitfalls in implementing accessible web and program design. You will walk away with actionable tips to use in your development projects.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34589", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34589", "speakers": [2699,173303], "categories": [ - + "Tools & Techniques", - + "User Experience" - + ] }, - + { "serial": 34595, "name": "Robots in Finland: How a Small Open Hardware Project Sparked International Collaboration Across 10 Timezones and 5,000 Miles ", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1464, "description": "This talk shares the success story of how a small open hardware project used an Arduino/Python archival digitization robot to spark an international collaboration spanning cultures and continents. The talk focuses on how the collaboration came to be, how the teams used tools like 3D printing and video to work together across 5000+ miles, and how other OS projects can create similar partnerships.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34595", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34595", "speakers": [165643], "categories": [ - + "Community" - + ] }, - + { "serial": 34610, "name": "DNSSEC Via a New Stub Resolver", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1466, "description": "The need for secure DNS is more pressing than ever but the current standard API for using the DNS can't take advantage of modern DNS features. We will give an application developers view of DNSSEC and describe the independently written getDNS API specification. We will showcase the open source implementation of the specification built by our team of developers from NLNet Labs and Verisign.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34610", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34610", "speakers": [173324,173326,173325,172895], "categories": [ - + "Security" - + ] }, - + { "serial": 34612, "name": "Sepia: How LinkedIn Mobile Made Integration Testing Fast and Reliable in Node.js", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1449, "description": "LinkedIn runs a node.js server to power its phone clients. Because the server makes HTTP requests to other services, network latencies make for slow, and potentially unreliable end-to-end tests. This presentation walks through how LinkedIn built an open-source tool, sepia, to address the challenge of scaling a complex test infrastructure in order to release high quality code with high confidence.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34612", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34612", "speakers": [172988], "categories": [ - + "Mobile Platforms" - + ] }, - + { "serial": 34627, "name": "Creating an SDK - Lessons Learned", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1459, "description": "Taking a complex API and wrapping it to create a coherent SDK for a programming language is a huge undertaking, and even harder when it has to be done by a single developer. I created pyrax, the Python SDK for OpenStack, at the request of my company. It has been a success, but it didn't come easy. In this talk I'll share some of the many lessons learned, both technical and political.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34627", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34627", "speakers": [152106], "categories": [ - + "Cloud" - + ] }, - + { "serial": 34630, "name": "Working with Design in Open Source", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1464, "description": "In this session we'll look at how design effects an open source project and how to encourage designers to contribute. We'll also cover the fundamentals of design, in case a developer finds themselves in the role of designer.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34630", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34630", "speakers": [173248], "categories": [ - + "Community", - + "User Experience" - + ] }, - + { "serial": 34632, "name": "OpenStack :: Where Continuous Delivery and Distros Collide", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1459, "description": "Mark McLoughlin and Monty Taylor - both members of the OpenStack Technical Committee and Foundation Board - gives their perspectives on how OpenStack caters to two distinct audiences with its time-based release process and its support for continuous deployment. They will also talk to this a case study for how DevOps is influencing the way open-source projects are managed and consumed.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34632", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34632", "speakers": [172824,109289], "categories": [ - + "Cloud" - + ] }, - + { "serial": 34635, "name": "Move Fast and Ship Things ", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1449, "description": "This talk will explore the 'move fast' side of Facebook\u2019s software engineering culture: development process, organizational structure, and the vast amounts of tooling we create and use to make sure we don\u2019t screw up. We\u2019ll also dig into how we 'ship things': release process, A/B testing, gate keepers, test infrastructure, and more.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34635", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34635", "speakers": [109270], "categories": [ - + "Cloud", - + "Operations & System Administration" - + ] }, - + { "serial": 34637, "name": "Chef and OpenStack", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1459, "description": "The open source configuration management and automation framework Chef is used to configure, deploy and manage many large public and private installations of OpenStack and supports a wide variety of integration opportunities. OpenStack is a large and complex ecosystem, this session will highlight the Chef resources available for developers and operators.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34637", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34637", "speakers": [141169], "categories": [ - + "Operations & System Administration" - + ] }, - + { "serial": 34640, "name": "Include Hack - HHVM - PHP++", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1458, "description": "Did you know that one of the biggest PHP sites on the internet isn't running PHP? Did you know that HHVM clocks in at anywhere between 2x and 10x faster than standard PHP with an Opcode Cache? Come take a look at \u201cThe other PHP engine\u201d, how to get a server up and running, what pitfalls to watch out for in migrating over, and what exciting extras are waiting. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34640", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34640", "speakers": [173262,173336], "categories": [ - + "PHP" - + ] }, - + { "serial": 34642, "name": "Playing Chess with Companies", "event_type": "tutorial", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 12:30:00", "venue_serial": 1458, "description": "Most organisations have strategy documents full of implementation, purchasing, tactical and operational choices. Remove this and you're often left with a vague 'why' which normally boils down to copying everyone else. In this tutorial I'll demonstrate how a large number of companies are playing a game of chess in which they can't see the board and how you can exploit this.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34642", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34642", "speakers": [6219], "categories": [ - + "Business" - + ] }, - + { "serial": 34646, "name": "Predicting Global Unrest with GDELT and SQL on Hadoop ", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1454, "description": "The Global Database of Events, Language, and Tone (GDELT) is an initiative to construct a catalog of human societal-scale behavior and beliefs across all countries of the world. Analysis of this data set requires addressing typical data quality and data skew issues. \r\n\r\nUse a combined Hadoop + SQL on Hadoop stack to cleanse the data and deliver insights into the state of the world. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34646", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34646", "speakers": [53442], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 34650, "name": "Arduino Yun for Intermediate Arduino Users: Using the Onboard Linux Computer to Communicate with Other Computers and the Internet", "event_type": "tutorial", - + "time_start": "2014-07-20 13:30:00", "time_stop": "2014-07-20 17:00:00", "venue_serial": 1470, "description": "The new Arduino Yun contains both an Arduino Leonardo and a full Linux system on a chip with built-in Ethernet and Wifi. This intermediate level hands-on tutorial will teach you how to use the Yun to communicate between Yun and Yun, Yun and laptop, and Yun and internet services, such Gmail, Twitter, and other services with APIs", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34650", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34650", "speakers": [141561], "categories": [ - + "Education", - + "Open Hardware" - + ] }, - + { "serial": 34654, "name": "Red October: Implementing the Two-man Rule for Keeping Secrets", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1462, "description": "Red October is an open source encryption server with a twist -- it can encrypt secrets, requiring more than one person to decrypt them. This talk will describe what goes into building an open source security product and using it in the real world. From motivation, design decisions, pitfalls of using a young programming language like Go, through deployment and opening the work up to the community.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34654", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34654", "speakers": [164229], "categories": [ - + "Security" - + ] }, - + { "serial": 34668, "name": "OpenUI5 - The New Responsive Web UI Library", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1450, "description": "OpenUI5 is a comprehensive enterprise-grade HTML5 UI library (developed by SAP) which has been open-sourced recently. Explore its power through concrete code examples and demos for key features like declarative UIs, data binding, and responsiveness: write ONE app and it will adapt to any device, from desktop screen to smartphones.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34668", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34668", "speakers": [170822,173233], "categories": [ - + "User Experience" - + ] }, - + { "serial": 34677, "name": "Elasticsearch: The Missing Tutorial", "event_type": "tutorial", - + "time_start": "2014-07-20 09:00:00", "time_stop": "2014-07-20 12:30:00", "venue_serial": 1452, "description": "Elasticsearch provides a powerful combination of clustered full-text search, synonyms, faceting, and geographic math, but there's a big gap between its documentation and real life. We'll tell hard-won war stories, work through hands-on examples, and show what happens behind the scenes, leaving you equipped to get the best use out of Elasticseach in your projects.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34677", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34677", "speakers": [173247,150], "categories": [ - + "Cloud", - + "Databases & Datastores" - + ] }, - + { "serial": 34678, "name": "Open HeARTware with ChickTech", "event_type": "tutorial", - + "time_start": "2014-07-20 13:30:00", "time_stop": "2014-07-20 17:00:00", "venue_serial": 1451, "description": "Are you a software person? An artsy type? Never thought you would like hardware? Or perhaps you love hardware? No matter what your skill level, this workshop is for you. Get in on the open hardware movement and join ChickTech to create your own \u201csoft circuit\u201d using conductive thread, fabric, inputs/outputs, and a microcontroller! ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34678", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34678", "speakers": [131890,124700], "categories": [ - + "Education" - + ] }, - + { "serial": 34687, "name": "Identity Crisis: Are We Really Who We Say We Are?", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1456, "description": "Karen Sandler, Executive Director of the GNOME Foundation, will discuss the peculiar tension in the intersection of free and open source software and corporate interest. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34687", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34687", "speakers": [173364], "categories": [ - + "Business", - + "Community" - + ] }, - + { "serial": 34688, "name": "Healthcare for Geeks", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1448, "description": "Hacking Healthcare author David Uhlman will show you how to 3D print your body parts, order your own lab work, build a DNA analyzer, tour an array of personal monitoring devices for fitness, health and open biology projects, stop eating altogether by switching to soylent. Also tips on what insurance to get, navigating hospitals and finding the right doctor.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34688", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34688", "speakers": [86111], "categories": [ - + "Main Stage" - + ] }, - + { "serial": 34690, "name": "Contributing to Contributors: Breaking Down the Barriers to First-commit", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1448, "description": "Every open source project is a unique snowflake of technology choices, coding style, and communication channels. Learning not only how, but the 'correct' way to contribute to each new project can be a blocker for would-be contributors. This talk will give practical examples of how you can reduce the learning curve for new contributors and improve the quality of first-commits.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34690", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34690", "speakers": [104522], "categories": [ - + "Main Stage" - + ] }, - + { "serial": 34695, "name": "Open edX: an Open-source MOOC Platform", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1462, "description": "Open edX is an open-source platform for delivering online courses. It's in use by the 31 member universities of edx.org (Harvard, MIT, Berkeley, etc), as well as Stanford, Google, and many other colleges and universities. This talk will describe the platform and show you ways to participate, as a course author, tool developer, or offering institution.\r\n ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34695", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34695", "speakers": [41059,179963], "categories": [ - + "Education" - + ] }, - + { "serial": 34700, "name": "PHP 5.6 and Beyond: Because Incrementing Major Versions is for Suckers", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1458, "description": "PHP 5.6 is out, and comes with useful new features and internal cleanups, as the last few 5.x releases have. In this talk, I'll discuss those features, but also where PHP is going: will there be a PHP 6 or 7 in the near future? What might it contain? How can we learn from Python 3 and Perl 6?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34700", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34700", "speakers": [152118], "categories": [ - + "PHP" - + ] }, - + { "serial": 34702, "name": "HTML5 Video Part Deux; New Opportunities and New Challenges", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1450, "description": "This talk gives a close look at second wave HTML5 features around video delivery \u2014 specifically, mediaSource API / adaptive streaming, encrypted media extension and WebRTC. We look at open tools and techniques for transcending platform limitations and delivery these experiences across increasingly diverse set of devices with real world examples from Kaltura, Wikimedia and others.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34702", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34702", "speakers": [108736], "categories": [ - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34705, "name": "What is Happening at the CentOS Project?", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1457, "description": "What is the future of CentOS Linux? Hear the true story from the project leaders behind the surprise announcement that the CentOS Project and Red Hat are joining forces.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34705", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34705", "speakers": [46737,173380,173381], "categories": [ - + "Community" - + ] }, - + { "serial": 34711, "name": "Schemas for the Real World", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1475, "description": "Development challenges us to code for users\u2019 personal world. Users give push-back to ill-fitted assumptions about their own name, gender, sexual orientation, important relationships, & other attributes that are individually meaningful. We'll explore how to develop software that brings real world into focus & that allows individuals to authentically reflect their personhood & physical world.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34711", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34711", "speakers": [141590], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 34713, "name": "Functionally Mobile (Automation)", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1451, "description": "Mobile's here to stay! This talk will showcase how Open Source tools can power your test automation for mobile apps. It entirely relies on Open Source components such as Appium, Cordova/PhoneGap an Topcoat.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34713", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34713", "speakers": [173378], "categories": [ - + "Mobile Platforms" - + ] }, - + { "serial": 34717, "name": "Developing High Performance Websites and Modern Apps with JavaScript and HTML5", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1450, "description": "Creating high performance sites and apps is crucial for every developer. In this session, we will explore the best practices and performance tricks, to make your apps running faster and fluid. Come learn the tips, tricks, and tools for maximizing the performance of your sites and apps with JavaScript and HTML5.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34717", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34717", "speakers": [133360], "categories": [ - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34718, "name": "Android Developer Tools Essentials", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1466, "description": "This session is an overview of the Android Developer Tools (ADT and Android Studio), including many useful techniques, tips and tricks for getting the most out of them.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34718", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34718", "speakers": [150073], "categories": [ - + "Mobile Platforms" - + ] }, - + { "serial": 34731, "name": "How We Built a Cloud Platform Using Netflix OSS", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1459, "description": "\r\nThe Netflix OSS Cloud stack is clearly a great set of components for building a cloud infrastructure and platform\u2014if you are Netflix. But how does that architecture work for other businesses? Learn how at Riot we leveraged Netflix OSS cloud tools and platform components to create an infrastructure for our global game platform\u2014maybe it can work for you too.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34731", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34731", "speakers": [161577], "categories": [ - + "Cloud" - + ] }, - + { "serial": 34737, "name": "Making Federal Regulations Readable with Python", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1465, "description": "The Consumer Financial Protection Bureau (http://cfpb.gov) has\r\ndeveloped an open source web-based tool to make regulations easy to\r\nread, access and understand. We talk about the unique parsing and\r\nother challenges we encountered working with these legal documents,\r\nand how we used Python, pyParsing, Django and other open source tools\r\nto solve them.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34737", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34737", "speakers": [157931], "categories": [ - + "Python" - + ] }, - + { "serial": 34744, "name": "Machine Learning for Rubyists", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1466, "description": "In this presentation we'll cover five important machine learning techniques that can be used in a wide range of applications. It will be a wide and shallow introduction, for Rubyists, not mathematicians - we'll have plenty of simple code examples. \r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34744", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34744", "speakers": [173396], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 34745, "name": "Secure Development is Much Easier Than You Think", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1462, "description": "Secure software development is something absolutely critical to helping create safer more trusted computing experiences for everyone.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34745", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34745", "speakers": [173399,173403], "categories": [ - + "Security" - + ] }, - + { "serial": 34746, "name": "Making maps with OpenStreetMap and Koop", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1450, "description": "Like maps and open data? Koop has created a new way of accessing open data and making cool maps with wide variety of data \r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34746", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34746", "speakers": [108520,173406], "categories": [ - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34753, "name": "International Community Building: Bridging Japan and the Rest of the World", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1464, "description": "Japan has a thriving open source technology community. It\u2019s also the third largest IT market in the world, with more engineers per capita than the US, and a history of game-changing open source projects like Ruby and Jenkins. Hear a first hand account of managing and cultivating open source communities in Japan, the US and other countries and discuss international community building.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34753", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34753", "speakers": [153857], "categories": [ - + "Community" - + ] }, - + { "serial": 34756, "name": "Graph Theory You Need to Know", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1452, "description": "A brief and friendly tour of the basics of graph theory, including a description and classification of the kinds of graphs and some interesting problems they can be employed to solve.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34756", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34756", "speakers": [137697], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 34757, "name": "The Data Structures (You Think) You Need to Know", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1448, "description": "A fun and approachable tour of some otherwise intimidating data structures. Learn how to solve difficult problems efficiently through the clever organization and linking of data.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34757", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34757", "speakers": [137697], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 34769, "name": "Tapping into Ruby from the JVM", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1456, "description": "With the diversity and innovation in the Ruby ecosystem and the popularity of polyglot programming on the robust and efficient JVM, Ruby and the JVM make a great fit. We\u2019ll cover numerous ways to invoke Ruby from Java and other JVM languages and how to package and deploy this style of application. We\u2019ll study examples from AsciidoctorJ, a Java API to the Ruby-based text processor, Asciidoctor.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34769", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34769", "speakers": [117513], "categories": [ - + "Ruby" - + ] }, - + { "serial": 34772, "name": "Introduction to Ceph", "event_type": "tutorial", - + "time_start": "2014-07-20 13:30:00", "time_stop": "2014-07-20 17:00:00", "venue_serial": 1458, "description": "This Introduction to Ceph tutorial will include a mix of lecture and instructor-led demonstrations that will introduce students to the Ceph distributed storage system, the challenges it addresses, its architecture, and solutions it offers.\r\n\r\nStudents will leave understanding how Ceph works, how it can be integrated with your services and applications, and how it works alongside OpenStack.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34772", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34772", "speakers": [183246], "categories": [ - + "Databases & Datastores", - + "Operations & System Administration" - + ] }, - + { "serial": 34773, "name": "Demystifying SELinux Part II: Who\u2019s Policy Is It Anyway?", "event_type": "tutorial", - + "time_start": "2014-07-20 09:00:00", "time_stop": "2014-07-20 12:30:00", "venue_serial": 1458, "description": "Building on last year\u2019s critically acclaimed \u2018Demystifying SELinux: WTF is it saying?\u2019 talk Demystifying \u2018SELinux Part II: Who\u2019s policy is it anyway?\u2019 is an extended tutorial which has attendees work through real life examples of SELinux configuration and policy construction.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34773", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34773", "speakers": [151833], "categories": [ - + "Operations & System Administration", - + "Security" - + ] }, - + { "serial": 34788, "name": "Painlessly Functional and Concurrent: An Introduction to Elixir", "event_type": "tutorial", - + "time_start": "2014-07-20 13:30:00", "time_stop": "2014-07-20 17:00:00", "venue_serial": 1457, "description": "This tutorial is a quick introduction to the Elixir programming language. We\u2019ll explore the basics of the language, meta programming, and explore why you want to use Elixir to write concurrent, scalable, and robust programs.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34788", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34788", "speakers": [173421], "categories": [ - + "Emerging Languages", - + "Tools & Techniques" - + ] }, - + { "serial": 34797, "name": "Open Mobile Accessibility with GitHub and Cordova", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1454, "description": "The story of an open-source project that brings mobile accessibility APIs together with native webviews to make mobile app development more responsive to users with disabilities.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34797", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34797", "speakers": [17378], "categories": [ - + "Mobile Platforms" - + ] }, - + { "serial": 34808, "name": "Writing English ", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1448, "description": "So you know Java, Scala, Python, and Perl, but do you know the correct usage of a semicolon when it comes to the English language? Writers and engineers alike often fall victim to grammatical blunders that can obscure their intended message. Fortunately, there are some simple ways of spotting and correcting these errors. Once learned, your writing will improve and your readers will thank you. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34808", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34808", "speakers": [173393], "categories": [ - + "Main Stage" - + ] }, - + { "serial": 34809, "name": "Streaming Predictions of User Behavior in Real-Time", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1452, "description": "Visitors to an online store rarely make their intention explicit. A valuable goal in digital marketing is to infer this intention so to influence the visitor's behavior in-situ. We describe a data-driven approach to identifying and predicting online user behavior. The talk focuses on the construction of real-time machine learning tools for inference to sites with thousands of concurrent visitors.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34809", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34809", "speakers": [173414,173431], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 34811, "name": "Digital Dancing", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1448, "description": "You check out the schedule, and you note with excitement that there's a presentation called DIGITAL DANCING. You grab your stuff and head for that conference room.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34811", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34811", "speakers": [161486,182741], "categories": [ - + "Main Stage" - + ] }, - + { "serial": 34814, "name": "Why You Should Be Looking at Functional Web Development", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1466, "description": "Web combined with functional programming gives pure awesomeness. Come and learn about WebSharper, an open source web development framework for F#, and how it makes programmers happier and more productive.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34814", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34814", "speakers": [132323], "categories": [ - + "Emerging Languages" - + ] }, - + { "serial": 34820, "name": "The State of Crypto in Python", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1465, "description": "Python has a complex past with crypto. There are half a dozen frameworks built on at least three separate C implementations, each with their own strengths and weaknesses and in various states of maintenance. This presentation will review the current state of the art and discuss the future of crypto in Python including a new library aimed at fixing modern crypto support in Python.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34820", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34820", "speakers": [173432,173435], "categories": [ - + "Python" - + ] }, - + { "serial": 34823, "name": "Building a Culture of Continuous Learning", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1462, "description": "Technology moves too quickly for us to ever really stop learning - but how can we establish and maintain a culture of continuous learning in our business teams? And how can we ensure that continuous learning is effective?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34823", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34823", "speakers": [152299], "categories": [ - + "Business", - + "Education" - + ] }, - + { "serial": 34824, "name": "Getting Started with OpenStack: Hands on Tutorial", "event_type": "tutorial", - + "time_start": "2014-07-20 09:00:00", "time_stop": "2014-07-20 12:30:00", "venue_serial": 1475, "description": "Curious about OpenStack, but don't know where to start? In this hands on tutorial we will walk you through the basics of OpenStack, the OpenSource cloud computing platform that is used to build private and public clouds. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34824", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34824", "speakers": [169647,169673], "categories": [ - + "Cloud" - + ] }, - + { "serial": 34829, "name": "Building Reliable Systems: Lessons from Erlang", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1452, "description": "Erlang is famous for building systems that are incredibly reliable, having virtually no down time! What are the principles that Erlang uses? Can we apply them in other languages? In this presentation, you'll learn how Erlang's design enables reliability and how you can use similar patterns to improve your own software and software systems.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34829", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34829", "speakers": [131729], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 34831, "name": "Kinect for Creative Development with Cinder, openFrameworks", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1458, "description": "Want to integrate some body, face, voice recognition into your 3D application? You can do this fairly easily using the Kinect for Windows sensor along with the Kinect Common Bridge, an open source library that makes it simple to integrate Kinect experiences into your C++ code/library. OpenFrameworks, Cinder and other creative development communities have adopted it already! Cool creative demos!", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34831", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34831", "speakers": [143232,23017], "categories": [ - + "User Experience" - + ] }, - + { "serial": 34836, "name": "Perl Lightning Talks", "event_type": "Event", - + "time_start": "2014-07-23 19:30:00", "time_stop": "2014-07-23 20:30:00", "venue_serial": 1450, "description": "Join us for the ever popular Perl Lightning Talks.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34836", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34836", "speakers": [4429], "categories": [ - + "Events", - + "Perl" - + ] }, - + { "serial": 34839, "name": "Moving Java into the Open", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1456, "description": "This session will explore how Java development has been brought into the open over the past several years. Several Java developer community efforts have brought open source development processes and new levels of transparency and participation into their communities. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34839", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34839", "speakers": [65329,116276], "categories": [ - + "Java & JVM" - + ] }, - + { "serial": 34840, "name": "Open Community Infrastructure How-to", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1466, "description": "Does every open source project need an open infrastructure? Should root be potentially available to any community member? If you think, 'Maybe, yes,' come learn how-to and why-to with lessons-learned from Fedora, oVirt, CentOS Project, and other projects.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34840", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34840", "speakers": [46737], "categories": [ - + "Operations & System Administration" - + ] }, - + { "serial": 34844, "name": "Pro Puppet", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1454, "description": "Learn to use Puppet like a Pro! We will take you through several examples of how to bring your Puppet deployment to the next level. We will cover Hiera, deploying puppet code, code architecture best practices, and integrating external tools.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34844", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34844", "speakers": [125113,99817], "categories": [ - + "Operations & System Administration", - + "Tools & Techniques" - + ] }, - + { "serial": 34845, "name": "Transit Appliance at Three", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1451, "description": "At OSCON 2011 we introduced the Transit Appliance, a project to use open hardware, open source software and open APIs to create a low-cost display for transit arrivals. Three years later we have two dozen displays deployed in the community, have seen the retirement of the Chumby, the rise of the Raspberry Pi and many new web services enriching the display. Progress and lessons learned.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34845", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34845", "speakers": [109116], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 34849, "name": "tmux - A Multiplexer's Multiplexer", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1466, "description": "Many developers, system/network admins, and designers spend good portions of their careers avoiding any interaction with their systems' command line interface(s) (CLI's). Unfortunately, the CLI is viewed as an archaic and inefficient means of being productive. In tmux, a powerful terminal multiplexer, developers and admins have a tool for more fully exploiting the power of the CLI.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34849", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34849", "speakers": [138530], "categories": [ - + "Operations & System Administration" - + ] }, - + { "serial": 34853, "name": "Build Your Own Exobrain", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1448, "description": "Online services like 'If This Then That' (IFTTT) are great for automating your life. However they provide limited ways for the end-user to add their own services, and often require credentials that one may normally wish to keep secret.\r\n\r\nThe 'exobrain' project allows for service integration and extension on a machine *you* control.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34853", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34853", "speakers": [6631], "categories": [ - + "Main Stage" - + ] }, - + { "serial": 34855, "name": "A Platform for Open Science", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1466, "description": "This talk will introduce a new open source platform for citizen science data. It allows anyone anywhere to create online data sets by uploading data from their own environmental sensors, mobile devices, do-it-yourself science equipment, and other measurement tools. The talk will describe the design and use of the platform, covering multiple applications and alternatives.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34855", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34855", "speakers": [169557], "categories": [ - + "Education" - + ] }, - + { "serial": 34856, "name": "Grow Developers. Grow Diversity. ", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1464, "description": "The United States needs more tech talent. Period.\r\n\r\nAnd yet there is a solution \u2014 Grow Developers. My talk will cover all the many ways this community can actively solve the lack of talent problem, and at the same time give solutions for also growing the female and minority tech populations.\r\n\r\nPeople will walk away with a 3-tiered approach for growing developers and growing diversity. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34856", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34856", "speakers": [173388], "categories": [ - + "Community" - + ] }, - + { "serial": 34860, "name": "From Madison Avenue to git Checkout -- A Journey", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1457, "description": "This talk is going to provide insight into what\u2019s it\u2019s like to view Software Development as an outsider, who happens to be an experienced successful professional. I will also tackle the issues that are implicit with imposter syndrome, such as how to grow developers, how to grow diversity, fixing broken hiring practices, and what it\u2019s like to be afraid of open source. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34860", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34860", "speakers": [173388,180122], "categories": [ - + "Community", - + "Education" - + ] }, - + { "serial": 34863, "name": "Keeping Open Source Open", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1464, "description": "What happens to an open source community full of hobbyists when the project cleans up its pile of spaghetti and chooses to adopt widely held programming paradigms and systems?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34863", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34863", "speakers": [173433], "categories": [ - + "Community" - + ] }, - + { "serial": 34865, "name": "Installing OpenStack using SaltStack", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1459, "description": "OpenStack is an open source implementation of cloud computing, potentially at very large scale. However, it has many moving parts and is complex to operate. \r\nSaltStack appears to provide scalable and secure orchestration for OpenStack.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34865", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34865", "speakers": [143135], "categories": [ - + "Cloud" - + ] }, - + { "serial": 34870, "name": "Open Source: Emerging in Asia / The Asian Open Source Report Card ", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1464, "description": "Asia is a huge untapped market for open source expansion, but it is very unlike North America or Europe. Learn differences within Asia, see open source proliferation in all markets, and most importantly get into thinking Asia expansion is prime.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34870", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34870", "speakers": [147], "categories": [ - + "Business", - + "Community" - + ] }, - + { "serial": 34873, "name": "Just Enough Math", "event_type": "tutorial", - + "time_start": "2014-07-20 09:00:00", "time_stop": "2014-07-20 12:30:00", "venue_serial": 1471, "description": "Advanced math for business people: \u201cjust enough math\u201d to take advantage of new classes of open source frameworks. Many take college math up to calculus, but never learn how to approach sparse matrices, complex graphs, or supply chain optimizations. This tutorial ties these pieces together into a conceptual whole, with use cases and simple Python code, as a new approach to computational thinking.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34873", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34873", "speakers": [146540], "categories": [ - + "Business", - + "Computational Thinking" - + ] }, - + { "serial": 34875, "name": "Money Machines", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1457, "description": "Money Machines are small scale highly technical or craft based entrepreneurial excursions. The purpose of the Money Machine is to empower individuals with tools which will allow her/him/them to follow their geeky and nerdy passion of passions while enabling them to address the various financial necessities of life. Money Machines allow people to work in the manner of their choosing.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34875", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34875", "speakers": [138530], "categories": [ - + "Business" - + ] }, - + { "serial": 34881, "name": "Building a Massively Scalable Web Server In Erlang", "event_type": "tutorial", - + "time_start": "2014-07-21 13:30:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1458, "description": "Learn the fundamentals of Erlang - a high productivity, functional programming language used to build scalable, highly concurrent systems. In this tutorial, we'll introduce Erlang by way of a fun problem: building an HTTP server! You'll learn the basic of networking programming in Erlang along with key techniques for performance and scalability.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34881", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34881", "speakers": [131729], "categories": [ - + "JavaScript - HTML5 - Web", - + "Tools & Techniques" - + ] }, - + { "serial": 34882, "name": "Highly Functional Programming in Perl", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1465, "description": "Functional programming is everywhere, hiding between imperative procedures. Stateless code with no side-effects may seem academic, but practical application of functional techniques leads to fewer bugs and cleaner code. Functional thinking is useful whether you're wrestling with a mess of copy-pasta or doing test-first development on some new object library.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34882", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34882", "speakers": [6574], "categories": [ - + "Perl" - + ] }, - + { "serial": 34888, "name": "Cathedrals in the Cloud: Musings on APIs for the Web", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1459, "description": "With the rise of cloud-based services and Web APIs, it may be time to re-visit Raymond's 19 'lessons' from his book 'The Cathedral and the Bazaar' to see how they can be applied (and/or modified) to fit a world where much of the software we use is no longer installed locally and is often kept out of reach from most developers and users. \r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34888", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34888", "speakers": [108272], "categories": [ - + "Cloud" - + ] }, - + { "serial": 34894, "name": "Mobile and Multi-Device Web Development with Tritium", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1449, "description": "Tritium is a new open source language from the creator of the popular Sass and HAML languages that brings a modern approach to web development with transforms. In this talk, we'll introduce the Tritium language and the power of transform based approaches for separating content from presentation in the building of multi-device websites for desktops, smartphones, tablets, TVs, wearables and beyond.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34894", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34894", "speakers": [122599], "categories": [ - + "Emerging Languages" - + ] }, - + { "serial": 34897, "name": "My Friends Keep Leaving and it is Ruining Board Games Day", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1454, "description": "These days, moving away doesn\u2019t put too much of a dampener on staying in touch with your friends. Unfortunately, it has had a severe effect on my regular board games day.\r\nSo, I thought, why not solve this problem with telepresence board gaming? Can\u2019t be too hard! This session will cover what's been done, and what problems are still out there I have no idea about how to solve?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34897", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34897", "speakers": [173452], "categories": [ - + "Geek Lifestyle" - + ] }, - + { "serial": 34905, "name": "OAuth2: The Swiss-Army Framework", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1456, "description": "If your application doesn't have APIs, it's probably written in Cold Fusion. Every application has APIs, and APIs need authentication. See how OAuth2 is robust enough to satisfy the demands of the enterprise, while still serving the smallest of side projects. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34905", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34905", "speakers": [173314], "categories": [ - + "Security" - + ] }, - + { "serial": 34906, "name": "Mapbox: Building the Future of Open Mapping", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1457, "description": "Mapbox is leading the way in open mapping. Large companies are switching to OpenStreetMap and open source software for mapping. Learn how Mapbox is running a business like you would run an open source project and how it is succeeding in a field dominated by large, well-funded players by being open. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34906", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34906", "speakers": [104652], "categories": [ - + "Business" - + ] }, - + { "serial": 34913, "name": "Data Workflows for Machine Learning", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1452, "description": "Several frameworks have emerged for handling data workflows. Meanwhile, business use of Machine Learning is less about algorithms and more about leveraging workflows. This talk compares/contrasts different workflow approaches with focus on use cases, plus how some leverage the PMML open standard. Summary points build a scorecard for evaluating frameworks based on your use case needs.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34913", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34913", "speakers": [146540], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 34920, "name": "Bringing Banking to the Poor with the Help of AngularJS", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1457, "description": "AngularJS is one of the most widely adopted open source Javascript frameworks in recent times. We use it for a not-so-typical use case: web apps to deliver financial services to the poor. In this case-study session, we analyze the pros/cons of AngularJS, establish why it was right for us, and go over our experiences using this powerful lightweight framework which adds value to our community daily.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34920", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34920", "speakers": [173455], "categories": [ - + "Business", - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 34921, "name": "Hacking Radio for Under $10", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1465, "description": "We'll explore how to use an $8 dollar DVB-T TV dongle to monitor and capture various radio frequencies. The RTL-SDR library turns a cheap Realtek DVB-T into a very powerful Software Defined Radio receiver which can be used to inspect and hack various wireless protocols. All of the code is Free and Open Source so it runs on all platforms.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34921", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34921", "speakers": [77757], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 34922, "name": "Designing for Reuse: Creating APIs for the Future", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1459, "description": "Sometimes your API is meant for a small group and will live for only a short time. Other times, your aim is to create an interface that will have wide appeal and should last years into the future. \r\n\r\nThis talk shows you how to create and maintain an API that it can be both stable and vital well into the future.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34922", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34922", "speakers": [108272], "categories": [ - + "Cloud" - + ] }, - + { "serial": 34941, "name": "LoopBack: Open Source mBaaS", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1454, "description": "Parse is a popular mobile Backend-as-a-Service allowing mobile developers to use backend APIs in conjunction with mobile apps. LoopBack is an open source mBaaS implementation that offers all the same functionality, is written in Node.js, and can be extended with Node.js' community of over 50,000 modules.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34941", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34941", "speakers": [173465], "categories": [ - + "Mobile Platforms" - + ] }, - + { "serial": 34942, "name": "Forking Culture and Committing Ops in Government", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1464, "description": "Culture shift is a huge challenge in the public sector. I will walk through how the Consumer Financial Protection Bureau is able to successfully open source data, platforms, and standards.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34942", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34942", "speakers": [156591], "categories": [ - + "Community" - + ] }, - + { "serial": 34944, "name": "Open Source Tools for the Polyglot Developer", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1449, "description": "Developers, increasingly, need to work in several different development languages. It is hard enough to remember all the bits and pieces of the languages themselves, do you really need to know all the unique toolchains to make them work?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34944", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34944", "speakers": [141524], "categories": [ - + "Emerging Languages" - + ] }, - + { "serial": 34952, "name": "Writing Documentation that Satisfies Your Users", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1462, "description": "Documentation is paramount to increasing an open source project's adoption and growth. But writing good documentation is hard. Using examples from new and mature projects, we'll explore detailed tactics for selecting, prioritizing, outlining, and writing documentation targeted at multiple audiences.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34952", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34952", "speakers": [142111], "categories": [ - + "User Experience" - + ] }, - + { "serial": 34953, "name": "Adventures in the WebRTC Garden--or is it Wilderness?", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1454, "description": "The prospects and promise of webRTC--direct browser-to-browser multimedia communications--have led to an explosion of tools, both proprietary and Open Source. In this session we present an overview of a variety of tools vying for attention, along with a demonstration of the sipML Javascript toolkit, using webRTC-enabled browsers and the latest version of Asterisk.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34953", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34953", "speakers": [6921,173464,173466,173467,173468], "categories": [ - + "Emerging Languages" - + ] }, - + { "serial": 34954, "name": "Introduction to Advanced Bash Usage", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1475, "description": "Broad introduction to Bash features for users who want to go beyond simple command execution. Covered topics include builtins, keywords, functions, parameters (arguments, variables, arrays, special parameters), parameter expansion and manipulation, compound commands (loops, groups, conditionals), and brace expansion.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34954", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34954", "speakers": [173223], "categories": [ - + "Tools & Techniques" - + ] }, - + { "serial": 34955, "name": "Hacking the Kernel, Hacking Myself", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1451, "description": "I was chosen, out of eighteen successful applicants, to be one of four Linux kernel interns through the Gnome Outreach Program for Women. This is the story of my journey from a frustrated retail worker, dreaming of writing code for a living, to a full fledged kernel developer. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34955", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34955", "speakers": [140811], "categories": [ - + "Geek Lifestyle" - + ] }, - + { "serial": 35024, "name": "Nymote: Git Your Own Cloud Here", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1459, "description": "If you want to run your own Internet node, it requires gluing together an awful lot of software, and maintaining it. We'll show you a fresh approach: use the Mirage operating system to easily compile the protocols you need (DNS, HTTP, XMPP and IMAP) for your Internet presence into a type-safe unikernel, and deploy the whole thing using just Travis CI and Git directly on the cloud or on ARM.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35024", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35024", "speakers": [109140,159772], "categories": [ - + "Cloud" - + ] }, - + { "serial": 35027, "name": "Git and GitHub Essentials", "event_type": "tutorial", - + "time_start": "2014-07-20 09:00:00", "time_stop": "2014-07-20 12:30:00", "venue_serial": 1450, "description": "Learn everything you need to know from Git and GitHub to be the most effective member of your team, save yourself from any jam, and work with the rest of your team flawlessly.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35027", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35027", "speakers": [152215], "categories": [ - + "Tools & Techniques" - + ] }, - + { "serial": 35038, "name": "A Reactive Game Stack: Using Erlang, Lua and VoltDB to Enable a Non-Sharded Game World ", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1475, "description": "Discover why Electronic Arts goes Erlang and hear about a powerful, reactive server architecture that supports a highly concurrent, analyzable and secure simulation stack for gaming. Learn how to easily script composable entities using a server environment purpose-built for event-driven programming, which is scalable under load, resilient and enables evaluation of huge data sets in real-time.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35038", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35038", "speakers": [174072,174073], "categories": [ - + "Databases & Datastores" - + ] }, - + { "serial": 35367, "name": "Apache HTTP Server; SSL from End-to-End", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 11:00:00", "time_stop": "2014-07-24 11:40:00", "venue_serial": 1456, "description": "This presentation covers all aspects of configuring Apache HTTP Server for https/TLS, including ECC, RSA and DH keys and key strength, cipher suites, SSL session caches vs. session tickets, OCSP stapling and TLS virtual hostnames. These elements are integrated to provide perfect forward secrecy and meet modern best practices for both client and proxied connections.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35367", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35367", "speakers": [124630], "categories": [ - + "Security" - + ] }, - + { "serial": 35390, "name": "Building an Open Source Learning Thermostat", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1451, "description": "The Nest learning thermostat has won the hearts and minds of consumers everywhere by completely re-thinking how a thermostat works. In this session, we'll explore the Internet of Things by discussing how to build an amazing connected device using open source technology, with Spark's open source thermostat project acting as a case study.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35390", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35390", "speakers": [175040], "categories": [ - + "Open Hardware" - + ] }, - + { "serial": 35406, "name": "Pinto: Hassle-Free Dependency Management for Perl", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1451, "description": "Managing CPAN dependencies can be a major frustration for Perl developers. In this session, you'll discover how to easily manage those dependencies by creating a private CPAN repository with Pinto.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35406", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35406", "speakers": [173472], "categories": [ - + "Perl" - + ] }, - + { "serial": 35437, "name": "Open Source and the Enterprise", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1457, "description": "How can businesses take the best ideas from the open source community to improve their end product and the happiness of their developers? In this fireside-chat-styled session, Derek Sorkin from GitHub will talk with Tim Tyler about his experiences setting up a community inside Qualcomm that mimics an open source project. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35437", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35437", "speakers": [175353,175354], "categories": [ - + "Business" - + ] }, - + { "serial": 35444, "name": "HTML5/Angular.js/Groovy/Java/MongoDB all together - what could possibly go wrong?", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1448, "description": "It seems to have been a common theme amongst startups to create the MVP\r\n(Minimum Viable Product) in a language that facilitates rapid\r\nprototyping (for example Ruby), and then migrate to the JVM when the\r\napplication has proved itself and requires more in terms of stability\r\nand performance.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35444", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35444", "speakers": [132564], "categories": [ - + "JavaScript - HTML5 - Web" - + ] }, - + { "serial": 35455, "name": "CANCELLED: Using Multi-key AVL Trees to Simplify and Speed Up Complex Searches", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1457, "description": "Multiple indexes into data structures add complexity and slow down processing. A single multi-keyed AVL tree can allow complex searches to be constructed more easily and performed quickly, with a single O(lg N) lookup.\r\n\r\nIn this talk we will discuss how these trees work and how to implement them. Examples will be shown using python version 3, with C++ libraries for optimization of key routines.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35455", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35455", "speakers": [31044], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 35464, "name": "Real-time Engineering at Uber and the Evolution of an Event-Driven Architecture", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 17:00:00", "time_stop": "2014-07-23 17:40:00", "venue_serial": 1452, "description": "Uber is one of the fastest growing companies in the world and the real-time engineering team are responsible for their mission critical Node.js-powered systems. Learn how they are adapting their services to be autonomous, loosely-coupled and highly-available by applying the principles of event-driven architecture.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35464", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35464", "speakers": [175481], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 35493, "name": "Coder Decoder: Functional Programmer Lingo Explained, with Pictures", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1454, "description": "For the uninitiated, a conversation with functional programmers can feel like ground zero of a jargon explosion. In this talk Lambda Ladies Co-Founder Katie Miller will help you to defend against the blah-blah blast by demystifying several terms commonly used by FP fans with bite-sized Haskell examples and friendly pictures. Expect appearances by Curry, Lens, and the infamous M-word, among others.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35493", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35493", "speakers": [175488], "categories": [ - + "Computational Thinking" - + ] }, - + { "serial": 35511, "name": "Checking Your Privilege: A How-To for Hard Things", "event_type": "Keynote", - + "time_start": "2014-07-23 09:45:00", "time_stop": "2014-07-23 10:00:00", "venue_serial": 1525, "description": "The purpose of this talk is to reexamine the topic through the lens of concrete things individuals can do to check their privilege \u2013 and to put it to work serving themselves and others.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35511", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35511", "speakers": [8837], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 35544, "name": "A Multi-Platform Microsoft: Azure, ASP.NET, Open Source, Git and How We Build Things Now", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1448, "description": "A lot has changed at Microsoft. Azure has 1000 Linux VMs to choose from, there's RESTful APIs abound, and more OSS than ever before. What are Microsoft's web folks thinking and how are they developing software today? Is is a good thing?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35544", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35544", "speakers": [132865], "categories": [ - + "Main Stage" - + ] }, - + { "serial": 35684, "name": "Open Source Your Data Design Process", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1448, "description": "Design is a process, not a product. What processes do successful data designers follow, and how can we all benefit by open-sourcing our processes (to make better products)?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35684", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35684", "speakers": [147840], "categories": [ - + "Main Stage" - + ] }, - + { "serial": 35743, "name": "Anticipating the Future - An Introduction to Value Chain Mapping", "event_type": "Keynote", - + "time_start": "2014-07-23 09:30:00", "time_stop": "2014-07-23 09:45:00", "venue_serial": 1525, "description": "In this keynote, Simon will present the general principles of industry change and describe what can and cannot be predicted. He will then examine how companies can better understand the environment around them and by anticipating the nature of change then manipulate the market in their favor through open techniques.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35743", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35743", "speakers": [6219], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 35744, "name": "Threats", "event_type": "Keynote", - + "time_start": "2014-07-23 09:10:00", "time_stop": "2014-07-23 09:20:00", "venue_serial": 1525, "description": "What do you care about most in the worlds of software, the Net, and Life Online? Are you worried about it? Now is the time for sensible, reasonable, extreme paranoia.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35744", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35744", "speakers": [24978], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 35847, "name": "OSCON Kids Day (Sold Out)", "event_type": "Event", - + "time_start": "2014-07-20 09:00:00", "time_stop": "2014-07-20 17:00:00", "venue_serial": 1587, "description": "If you have a school aged children interested in learning more about computer programming, bring them to OSCON. We'll be hosting an entire day of workshops for kids about Java, Python, Scratch, Minecraft Modding, Arduino and more. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35847", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35847", "speakers": [], "categories": [ - + "Events" - + ] }, - + { "serial": 35848, "name": "Ignite OSCON (sponsored by Orly Atomics)", "event_type": "Event", - + "time_start": "2014-07-20 17:30:00", "time_stop": "2014-07-20 19:00:00", "venue_serial": 1525, "description": "If you had five minutes on stage what would you say? What if you only got 20 slides and they rotated automatically after 15 seconds? Would you pitch a project? Launch a web site? Teach a hack? We\u2019ll find out at our annual Ignite event at OSCON.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35848", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35848", "speakers": [], "categories": [ - + "Events" - + ] }, - + { "serial": 35853, "name": "OSCON 5K Glow Run and After Party", "event_type": "Event", - + "time_start": "2014-07-20 20:30:00", "time_stop": "2014-07-20 22:00:00", "venue_serial": 1606, "description": "Don't forget to pack your running shoes and your glow-in-the-dark gear, because the OSCON 5K fun run is back. Whether you are an avid runner or just starting out, you are invited to join other OSCON attendees Sunday evening for a run/jog/walk through some of the most scenic and emblematic sites of Portland. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35853", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35853", "speakers": [], "categories": [ - + "Events" - + ] }, - + { "serial": 35854, "name": "Expo Hall Opening Reception (sponsored by Bluehost)", "event_type": "Event", - + "time_start": "2014-07-21 17:00:00", "time_stop": "2014-07-21 18:00:00", "venue_serial": 1474, "description": "Grab a drink and kick off OSCON by meeting and mingling with exhibitors and fellow attendees.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35854", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35854", "speakers": [], "categories": [ - + "Events" - + ] }, - + { "serial": 35855, "name": "OSCON Elements Attendee Party", "event_type": "Event", - + "time_start": "2014-07-21 18:00:00", "time_stop": "2014-07-21 20:00:00", "venue_serial": 1585, "description": "This year's attendee party focuses on the four classical elements--fire, earth, air, and water. Wait till you see how each of these essential ideas transforms Hall B into new areas to explore and savor. Trust us, this is one party you don't want to miss! ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35855", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35855", "speakers": [], "categories": [ - + "Events" - + ] }, - + { "serial": 35856, "name": "Puppet Labs Party", "event_type": "Event", - + "time_start": "2014-07-21 20:00:00", "time_stop": "2014-07-21 22:00:00", "venue_serial": 1626, "description": "Join Puppet Labs for our OSCON \u201cOpen\u201d House Party! We are excited to open our doors to all our OSCON and Puppet Labs Friends.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35856", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35856", "speakers": [], "categories": [ - + "Events" - + ] }, - + { "serial": 35857, "name": "Booth Crawl", "event_type": "Event", - + "time_start": "2014-07-22 17:40:00", "time_stop": "2014-07-22 19:00:00", "venue_serial": 1474, "description": "Quench your thirst with vendor-hosted libations and snacks while you check out all the cool stuff in the expo hall. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35857", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35857", "speakers": [], "categories": [ - + "Events" - + ] }, - + { "serial": 35858, "name": "Citrix Open Cloud Poker Party", "event_type": "Event", - + "time_start": "2014-07-22 20:30:00", "time_stop": "2014-07-22 23:30:00", "venue_serial": 1473, "description": "*8:30pm - 12:00am*\r\n\r\nCitrix is sponsoring a night of poker, cocktails and hors d'oeuvres. For one night only, OSCON\u2019s Foyer will be transformed into Portland\u2019s only poker room complete with professional dealers. You'll be playing poker above the city lights with a perfect view of the city.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35858", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35858", "speakers": [], "categories": [ - + "Events" - + ] }, - + { "serial": 35859, "name": "Closing Get Together", "event_type": "Event", - + "time_start": "2014-07-24 13:15:00", "time_stop": "2014-07-24 14:00:00", "venue_serial": 1473, "description": "Take the opportunity to network one last time and exchange contact information with one another. Drinks and snacks provided. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35859", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35859", "speakers": [], "categories": [ - + "Events" - + ] }, - + { "serial": 35864, "name": "Something To Remember", "event_type": "Keynote", - + "time_start": "2014-07-23 09:05:00", "time_stop": "2014-07-23 09:10:00", "venue_serial": 1525, "description": "Keynote by Piers Cawley, Perl programmer, singer and balloon modeller.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35864", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35864", "speakers": [75349], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 35883, "name": "Open Source and Social Change \u2014 Making the World a Better Place", "event_type": "Keynote", - + "time_start": "2014-07-24 12:45:00", "time_stop": "2014-07-24 13:10:00", "venue_serial": 1525, "description": "Keynote by Paul Fenwick, managing director of Perl Training Australia.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35883", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35883", "speakers": [6631], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 35913, "name": "Introvert? Extrovert? Klingon? We've Got You Covered.", "event_type": "Keynote", - + "time_start": "2014-07-22 09:30:00", "time_stop": "2014-07-22 09:45:00", "venue_serial": 1525, "description": "Keynote by Wendy Chisholm, Senior Accessibility Strategist and Universal Design Evangelist, Microsoft.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35913", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35913", "speakers": [17417], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 35915, "name": "Opening Welcome and Keynotes", "event_type": "Keynote", - + "time_start": "2014-07-22 09:00:00", "time_stop": "2014-07-22 09:05:00", "venue_serial": 1525, "description": "Opening remarks by OSCON program chairs, Matthew McCullough, Sarah Novotny and Simon St. Laurent. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35915", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35915", "speakers": [74852,76338,3476], "categories": [ - + ] }, - + { "serial": 35950, "name": "Announcements & Keynotes", "event_type": "Keynote", - + "time_start": "2014-07-23 09:00:00", "time_stop": "2014-07-23 09:05:00", "venue_serial": 1525, "description": "Wednesday announcements and remarks by OSCON program chairs, Matthew McCullough, Sarah Novotny and Simon St. Laurent. We'll be announcing more keynote speakers here soon.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35950", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35950", "speakers": [76338,74852,3476], "categories": [ - + ] }, - + { "serial": 35951, "name": "Announcements & Keynotes", "event_type": "Keynote", - + "time_start": "2014-07-24 09:00:00", "time_stop": "2014-07-24 09:05:00", "venue_serial": 1525, "description": "Thursday announcements and remarks by OSCON program chairs, Matthew McCullough, Sarah Novotny and Simon St. Laurent. We'll be announcing more keynote speakers here soon.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35951", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35951", "speakers": [74852,76338,3476], "categories": [ - + ] }, - + { "serial": 35956, "name": "The Wonders of Programming", "event_type": "Keynote", - + "time_start": "2014-07-22 09:05:00", "time_stop": "2014-07-22 09:20:00", "venue_serial": 1525, "description": "Kids can start to learn to program at any age; I started at six. All I needed was tools, guidance, and encouragement. Once I got hooked, a whole new world of possibilities opened up for me. I could create my own video games instead of being restricted by the rules of games made by others. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35956", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35956", "speakers": [177245], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 35988, "name": "Discover OpenUI5 \u2013 The New Web UI library from SAP", "event_type": "Event", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 12:30:00", "venue_serial": 1462, "description": "OpenUI5 is a powerful web UI library from SAP that has recently entered the open source world. With OpenUI5 you can easily develop enterprise-grade responsive web applications that run on multiple platforms. It is based on many open source libraries. Start from scratch and learn how to build OpenUI5 applications in this tutorial.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35988", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/35988", "speakers": [173233,104828,170822], "categories": [ - + "Sponsored Tutorials" - + ] }, - + { "serial": 36005, "name": "Migrating Data from MySQL and Oracle into Hadoop", "event_type": "BoF", - + "time_start": "2014-07-22 20:00:00", "time_stop": "2014-07-22 21:00:00", "venue_serial": 1464, "description": "Open discussion to talk about the methods, tricks and different tools available for copying and replicating data from traditional RDBMS into Hadoop, covering best practices and customer stories.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36005", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36005", "speakers": [], "categories": [ - + ] }, - + { "serial": 36014, "name": "Hadoop Get-together", "event_type": "BoF", - + "time_start": "2014-07-22 19:00:00", "time_stop": "2014-07-22 20:00:00", "venue_serial": 1464, "description": "Do you Hadoop? Or at least interested in stories from people who do?\r\nWe will meet to share our experience and trade tips about working with\r\nthe open source data storage and processing framework - Hadoop.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36014", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36014", "speakers": [], "categories": [ - + ] }, - + { "serial": 36065, "name": "Docker BoF", "event_type": "BoF", - + "time_start": "2014-07-20 19:00:00", "time_stop": "2014-07-20 20:00:00", "venue_serial": 1607, "description": "Come join some of the team from Docker and get your questions answered and your problems resolved!", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36065", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36065", "speakers": [], "categories": [ - + ] }, - + { "serial": 36174, "name": "Distributed Caching", "event_type": "BoF", - + "time_start": "2014-07-22 20:00:00", "time_stop": "2014-07-22 21:00:00", "venue_serial": 1454, "description": "Caching at scale might be troublesome and sometimes require adapting your usage patterns to really take advantage of the employed solutions.\r\nMoving the business logic to the caching subsystem and allowing horizontal scaling might be a key factor to minimise the impact of introducing a caching layer in your infrastructure.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36174", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36174", "speakers": [], "categories": [ - + ] }, - + { "serial": 36180, "name": "Hands-on CloudStack Ecosystem Tutorial", "event_type": "Event", - + "time_start": "2014-07-21 13:30:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1462, "description": "Building a cloud is one part of the equation. To get work done with a cloud you need a solid ecosystem that goes with it. In this hands-on tutorial we go through some of the tools in the CloudStack ecosystem: Cloudmonkey, Libcloud, Vagrant, Ansible, and Ec2stack. Come ready to learn and use a cloud.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36180", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36180", "speakers": [152123], "categories": [ - + "Sponsored Tutorials" - + ] }, - + { "serial": 36202, "name": "Building an API for the Planet with a New Approach to Satellites", "event_type": "Keynote", - + "time_start": "2014-07-22 09:55:00", "time_stop": "2014-07-22 10:10:00", "venue_serial": 1525, "description": "Keynote by Will Marshall, CEO of Planet Labs. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36202", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36202", "speakers": [164144], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 36203, "name": "Google Summer of Code BOF", "event_type": "BoF", - + "time_start": "2014-07-20 19:00:00", "time_stop": "2014-07-20 20:00:00", "venue_serial": 1456, "description": "Meetup for students, mentors, and org admins who have participated or are participating in Google Summer of Code. Also come to learn more about Google Summer of Code if you think you might be interested in participating in a future year!", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36203", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36203", "speakers": [], "categories": [ - + ] }, - + { "serial": 36257, "name": "Yes, Your Refrigerator Is Trying To Kill You: Bad Actors and the Internet of Things", "event_type": "Keynote", - + "time_start": "2014-07-24 09:20:00", "time_stop": "2014-07-24 09:30:00", "venue_serial": 1525, "description": "As more and more atypical devices are internet enabled, operating system providers need to look at the longer term impacts and plan accordingly. How can CE manufactures keep devices up to date and secure over the lifetime of the device. What does it look like when we fail to plan to do so? How can the open source way solve some of these problems.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36257", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36257", "speakers": [178139], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 36404, "name": "ELK Stack BoF", "event_type": "BoF", - + "time_start": "2014-07-23 19:00:00", "time_stop": "2014-07-23 20:00:00", "venue_serial": 1460, "description": "If you use and love the ELK stack (that's Elasticsearch for search & analytics, Logstash for centralized logging & Kibana for beautiful visualizations), join us for an evening of discussion about these three open source tools and how they make developers and sysadmins lives way better. And your business humans, too. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36404", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36404", "speakers": [], "categories": [ - + ] }, - + { "serial": 36494, "name": "O'Reilly Open Source Awards", "event_type": "Keynote", - + "time_start": "2014-07-24 12:40:00", "time_stop": "2014-07-24 12:45:00", "venue_serial": 1525, "description": "The 10th Annual O\u2019Reilly Open Source Award winners will be announced. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36494", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36494", "speakers": [], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 36818, "name": "The Concert Programmer", "event_type": "Keynote", - + "time_start": "2014-07-24 09:05:00", "time_stop": "2014-07-24 09:20:00", "venue_serial": 1525, "description": "In this presentation Andrew will be live-coding the generative algorithms that will be producing the music that the audience will be listening too. As Andrew is typing he will also attempt to narrate the journey, discussing the various computational and musical choices made along the way. A must see for anyone interested in creative computing.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36818", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36818", "speakers": [178738], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 36819, "name": "Programmer, Program, Machine and Environment", "event_type": "40-minute conference session", - + "time_start": "2014-07-24 10:00:00", "time_stop": "2014-07-24 10:40:00", "venue_serial": 1448, "description": "In this talk, Andrew will delve deeper into the technical and philosophical underpinnings of live-coding as an artistic performance practice. Using his own Extempore language as a reference, Andrew will demonstrate, in a very hands on way, how live-coding works in practice, from both an end-user perspective, as well as a systems design perspective. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36819", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36819", "speakers": [178738], "categories": [ - + "Main Stage" - + ] }, - + { "serial": 36848, "name": "Mozilla Webmaker ", "event_type": "BoF", - + "time_start": "2014-07-20 19:00:00", "time_stop": "2014-07-20 20:00:00", "venue_serial": 1457, "description": "Webmaker is a collection of innovative tools and curricula for a global community that is teaching the web. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36848", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36848", "speakers": [], "categories": [ - + ] }, - + { "serial": 36849, "name": "Designing Projects for Particpation", "event_type": "BoF", - + "time_start": "2014-07-22 19:00:00", "time_stop": "2014-07-22 20:00:00", "venue_serial": 1461, "description": "Designing for Participation was created in order to get Mozilla community members to think about how they can structure the work Mozilla does to better enable contributions from anywhere. This BOF will lead discussion through a workshop designed by Mozilla to help project leaders Design projects for participation", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36849", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36849", "speakers": [], "categories": [ - + ] }, - + { "serial": 36890, "name": "Understanding Hypervisor Selection in Apache CloudStack", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1460, "description": "Apache CloudStack enables cloud operators to quickly create scalable clouds with support for multiple hypervisors. Choice is wonderful, but also requires an understanding of how hypervisor features integrate with CloudStack. In this session we'll look at the options and provide a template for deployment success.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36890", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36890", "speakers": [141574], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 36923, "name": "OSCON Town Hall", "event_type": "Keynote", - + "time_start": "2014-07-24 11:50:00", "time_stop": "2014-07-24 12:30:00", "venue_serial": 1464, "description": "OSCON belongs to its attendees, and we want to hear what you think of this year\u2019s show. Join the organizers to talk about what you loved and hated about OSCON, and what you\u2019d like to see next year.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36923", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36923", "speakers": [10,74852,76338,3476], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 36934, "name": "Open Cloud Day", "event_type": "Event", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1459, "description": "Open Cloud Day at OSCON will cover the latest innovations in public and private clouds, IaaS, and PaaS platforms. You'll learn from industry practitioners from a variety of platforms, who will share their expertise, and provide you with a vision of where open source in the cloud is heading. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36934", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/36934", "speakers": [], "categories": [ - + "Events", - + "Sponsored Tutorials" - + ] }, - + { "serial": 37002, "name": "Crash Course in Open Source Cloud Computing", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1461, "description": "The open source mantra is to release early and release often. That means software velocity can be difficult to keep up with. This discussion will expand on the latest open source software used to deliver and manage cloud computing infrastructure. Topics covered include virtualization (KVM, Xen Project, LXC), orchestration (OpenStack, CloudStack, Eucalyptus), and other complimentary technology.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37002", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37002", "speakers": [6653], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37014, "name": "OpenStack Redefining 'Core' Using Community, Tests and Selected Upstream Code", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 11:30:00", "time_stop": "2014-07-21 12:00:00", "venue_serial": 1584, "description": "Discuss how OpenStack works towards an interoperable open IaaS using a process we call DefCore. We'll review how we're creating a process that is open, collaborative, technically relevant and principles driven.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37014", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37014", "speakers": [122917], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37016, "name": "The quantitative state of the Open Cloud", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 15:45:00", "time_stop": "2014-07-21 16:15:00", "venue_serial": 1584, "description": "The talk will present a quantitative analysis of the projects producing the main free, open source software cloud platforms: OpenStack, Apache CloudStack, OpenNebula and Eucalyptus. The analysis will focus on the communities behind those projects, their main development parameters, and the trends that can be observed.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37016", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37016", "speakers": [173116], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37020, "name": "Building an Open Cloud From the Network Perspective ", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 16:15:00", "time_stop": "2014-07-21 16:45:00", "venue_serial": 1584, "description": "When designing and building a private cloud often times the network is not treated as a first class citizen and even sometimes dealt with as an after thought. This presentation will look at the process of building a cloud from the network view. We will look at some best practices for configuring servers and switches in a rack as the basis for the cloud deployment and on going management. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37020", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37020", "speakers": [122424], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37021, "name": "Behind the Scenes: How We Produce OpenStack", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 14:00:00", "time_stop": "2014-07-21 14:30:00", "venue_serial": 1584, "description": "The last OpenStack release was the result of the combined work of more than 1200 contributors. How can all those people be coordinated and deliver results on schedule, with no classic management structure ?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37021", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37021", "speakers": [109289], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37030, "name": "Open Source in Enterprise Software", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1461, "description": "This session is aimed at providing an understanding of why, where, and how SAP is engaged in adopting and leading Open Source projects in the enterprise software space. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37030", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37030", "speakers": [180019], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37039, "name": "Office Hour with Gwen Shapira (Cloudera) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 15:20:00", "time_stop": "2014-07-23 16:00:00", "venue_serial": 1546, "description": "Gwen\u2019s got answers when it comes to Hadoop, R, analytics and more. She\u2019s happy to talk to you about getting started with Hadoop; R, Python and data analysis with Hadoop; architecture, design, and implementation of Hadoop applications; and anything else you\u2019d like to bring up.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37039", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37039", "speakers": [126882], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37042, "name": "Office Hour with Jono Bacon (XPRIZE Foundation)", "event_type": "Office Hours", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1546, "description": "If you have questions about community management and leadership, hiring community managers, or managing community relationships, come by and talk to Jono. He\u2019ll discuss building collaborative workflow and tooling, conflict resolution and managing complex personalities, and building buzz and excitement around your community.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37042", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37042", "speakers": [108813], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37043, "name": "Office Hour with Stephen OSullivan (Silicon Valley Data Science) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 15:20:00", "time_stop": "2014-07-23 16:00:00", "venue_serial": 1547, "description": "Come by and talk to Stephen about anything related to big data, Hadoop, and creating scalable, high-availability, data and applications solutions\u2014including data pipelines, data platforms, and Fourier analysis. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37043", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37043", "speakers": [133624], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37044, "name": "Office Hour with Garth Braithwaite (Adobe)", "event_type": "Office Hours", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1546, "description": "Garth has found that the topic of open source is a bit controversial when people start talk about applying it to the visual and experience design process. He\u2019d like to talk with open source contributors about how the design process could be worked into specific projects, and the proposal (as well its rebuttals) that design should be done in the open.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37044", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37044", "speakers": [173248], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37045, "name": "Office Hour with Constantine Aaron Cois (Carnegie Mellon University, Software Engineering Institute) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1546, "description": "Constantine will be more than happy to discuss Node.js or anything else related to programming, software architecture, web apps, and other topics. Let\u2019s have some fun! Dive into Node.js programming and design patterns, event loops and high concurrency apps, reactive apps, and the Meteor framework.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37045", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37045", "speakers": [141235], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37046, "name": "Office Hour with Harrison Mebane (Silicon Valley Data Science) and Stephen OSullivan (Silicon Valley Data Science)", "event_type": "Office Hours", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1546, "description": "Can computers tell if trains run on time? Harrison and Stephen are ready to discuss this topic and other matters related to the Internet of Things and data\u2014including data pipelines, data platforms, and Fourier analysis.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37046", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37046", "speakers": [171621,133624], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37047, "name": "Office Hour with J\u00e9r\u00f4me Petazzoni (Docker Inc.) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1546, "description": "Jerome has worked with Docker since its inception, and before that, built the dotCloud PAAS. He\u2019ll be glad to share his experiences in those domains, and discuss everything about Docker and containers, including how to make containers secure; service discovery, network integration, scaling, and failover; and containers for desktop applications.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37047", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37047", "speakers": [151611], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37048, "name": "Office Hour with Heather VanCura (Java Community Process JCP) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1546, "description": "The Adopt-a-JSR and Adopt OpenJDK programs have gained worldwide community participation in the past two years. Come discuss the details with Heather and find out how you can contribute to better, more practical standards. She\u2019ll talk about upcoming changes aimed at broadening participation in the standards process. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37048", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37048", "speakers": [65329], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37049, "name": "Office Hour with Kara Sowles (Puppet Labs) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 15:20:00", "time_stop": "2014-07-22 16:00:00", "venue_serial": 1546, "description": "Kara is always eager to swap user group tips with you. Come by and pick her brain about ways to remove common obstacles that user groups encounter, and how to craft welcoming, friendly meetings and events. Find out how to plan the right events for your community.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37049", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37049", "speakers": [142767], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37051, "name": "Office Hour with Michael Bleigh (Divshot) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1546, "description": "Come by and talk to Michael about single-page web applications, static site generators, and static content hosting. He\u2019s also willing to tackle other issues such as CORS web services, browser technology, and web components.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37051", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37051", "speakers": [2593], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37052, "name": "Office Hour with Michael Hunger (Neo Technology) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1547, "description": "Graph Databases take a different approach than relational and aggregate-oriented NoSQL databases. They make connections cheap and fast by making them an explicit part and first-level citizen of your database. Michael will discuss graph-enabled applications, answer questions about their pros and cons, and help you model your domain and use-cases in an interactive way. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37052", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37052", "speakers": [159719], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37053, "name": "Office Hour with James Turnbull (Docker) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1548, "description": "If you\u2019re ready to learn about Docker for building, shipping, and running distributed applications, James is ready to talk Docker with you. You\u2019ll not only find out how to get started, but also learn about Docker use cases, the Docker roadmap, and how to integrate Docker with Puppet, Chef, SaltStack, or Ansible.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37053", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37053", "speakers": [5060], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37054, "name": "Office Hour with Catherine Farman (Happy Cog) and Corinne Warnshuis (Girl Develop It Philadelphia) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1546, "description": "You want to get more women involved in open source projects and communities. And Catherine and Corinne want to help. Come by find out how you can organize communities that are welcoming to women, and how you can be an ally to underrepresented minorities in tech. Start or help a Girl Develop It chapter, and learn about other nonprofits you can and should partner with both nationally and locally.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37054", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37054", "speakers": [169992,173025], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37055, "name": "Open Cloud Standards In The Real World", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 15:15:00", "time_stop": "2014-07-21 15:45:00", "venue_serial": 1584, "description": "In the range of API and protocol development from free-form to totally static, there is room in open clouds for patterns that allow both for dynamic discovery and predictable reuse. This talk highlights open cloud standards-based methods that have stood up to testing in the National Science Foundation's Cloud and Autonomic Computing Center Standards Testing Lab under real-world conditions.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37055", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37055", "speakers": [180050], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37056, "name": "Office Hour with Shadaj Laddad (School) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1579, "description": "Shadaj is a 14 year old student who loves to program. He\u2019s happy to chat about anything from Scala programming to technology in schools today. He\u2019ll answer your questions about kids programming, including how to get started and what tools are available. And he\u2019ll engage you on topics such bioinformatics algorithms, Android, web development, and game programming.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37056", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37056", "speakers": [177245], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37058, "name": "Open Is As Open Does", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 13:30:00", "time_stop": "2014-07-21 14:00:00", "venue_serial": 1584, "description": "'The Cloud' was built on Open Source. In no way does anything resembling IaaS 'cloud' exist without the foundation of freely available open source operating systems and virtualization, but what does 'Open' mean to the user of a service? Does it matter if the code behind a service is open? How would one even know? Do other aspects and definitions of openness become more important than source code?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37058", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37058", "speakers": [24052], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37059, "name": "Office Hour with Harry Percival (Harry Percival) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1547, "description": "Whether the testing goat is steering you right or wrong, you\u2019ll want to join Harry for an informal discussion of testing and TDD. Find out how to get started with testing and how to get the most value from it. Learn the relative merits of pure isolated unit tests, integrated tests, and functional tests. And explore how to adapt your approach for different project types.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37059", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37059", "speakers": [180056], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37061, "name": "Office Hour with Florian Haas (hastexo) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1546, "description": "Tap Florian\u2019s brain about all things OpenStack, and take advantage of his broad experience in real-life production OpenStack deployments. Whether it\u2019s OpenStack strategy, organizational concerns, or getting down into the nitty-gritty aspects of OpenStack technology, Florian is happy to talk to you about it. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37061", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37061", "speakers": [131884], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37063, "name": "Office Hour with A. Jesse Jiryu Davis (MongoDB) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1547, "description": "Come by and have an informal chat with Jesse about MongoDB and Python-related topics, such as Python\u2019s async frameworks, using MongoDB with Python, and MongoDB in general.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37063", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37063", "speakers": [172536], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37066, "name": "Office Hour with Matt Stine (Pivotal) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1546, "description": "Why would a Java developer want to learn the Go programming language? Matt is ready to have an informal chat with you about it. Find out how the idiomatic use of Go interfaces encourage you to structure your programs differently (as opposed to Java\u2019s object-oriented constructs), and how Go\u2019s built-in concurrency features make writing concurrent software accessible to mere mortals. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37066", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37066", "speakers": [173088], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37067, "name": "Office Hour with Anne Ogborn (Robokind) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1547, "description": "Ann is ready to talk SWI-Prolog with you\u2014and help you get it installed. She\u2019ll chat with you about your planned uses for Prolog, talk you out of the \u201cwrap my rules engine in a real language\u201d mentality, and help you get through the \u201chey, 2+2 = 4 fails, wtf?\u201d stage. And she\u2019ll laugh evilly when you mention Tomcat.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37067", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37067", "speakers": [172986], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37068, "name": "Office Hour with Matt Ray (Chef Software, Inc.) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1548, "description": "Chef questions? Bring any and all directly to Matt. He\u2019s ready to discuss how Chef is used to manage the deployment of OpenStack as well as the infrastructure on top of OpenStack, and how to get involved with the Chef and OpenStack community. Beyond that, the sky\u2019s the limit.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37068", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37068", "speakers": [141169], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37069, "name": "Office Hour with Phil Webb (Pivotal) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1547, "description": "If there\u2019s anything you want to know about Spring Boot or the Spring Framework in general, Phil is ready to help. He\u2019ll talk to you about an array of issues, such as developing micro-services with Spring, how Pivotal developed the new \u201cspring.io\u201d website, and how to become a full-stack Java developer.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37069", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37069", "speakers": [171565], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37071, "name": "Office Hour with Jamie Allen (Typesafe) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1547, "description": "Reactive applications are taking over the enterprise world. Join Jamie to chat about various Reactive topics. He\u2019ll answer questions including: How well do various technologies support the core Reactive tenets of event-driven, scalable, fault tolerant and responsive? How do you use open source technologies to deploy an elastically Reactive application? What is the role of big data in Reactive?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37071", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37071", "speakers": [170293], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37073, "name": "Office Hour with Scott Murray (University of San Francisco)", "event_type": "Office Hours", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1580, "description": "Scott\u2019s here to answer your questions about data and how to successfully conveying meaning through well-designed graphics. He\u2019ll chat with you about the data visualization design process; related technologies, including d3.js and Processing; and how we can all benefit by open-sourcing our processes. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37073", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37073", "speakers": [147840], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37075, "name": "Office Hour with Francesc Campoy (Google Inc.) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 15:20:00", "time_stop": "2014-07-23 16:00:00", "venue_serial": 1548, "description": "If you\u2019ve got questions about Go, you\u2019re in luck. Francesc will answer questions about Go basics, programming, organizing your code, and more. Find out about Go concurrency and concurrency primitives, and how Go interfaces simplify your code and break fictitious dependencies.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37075", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37075", "speakers": [155088], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37077, "name": "Office Hour with Kirsten Hunter (Akamai)", "event_type": "Office Hours", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1547, "description": "If you want your APIs to amaze and delight your developer partners, bring Kristen all of your API design questions. She\u2019ll chat with you about the API design process (how to plan and create an API that will be successful), developer engagement and support (aka the care and feeding of your developer partners), and how you can help your customers learn and troubleshoot your API.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37077", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37077", "speakers": [4265], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37078, "name": "Office Hour with Benjamin Kerensa (Mozilla) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1548, "description": "If you\u2019ve ever toyed with the idea of contributing to Firefox, Benjamin would love to talk to you about ways to make it happen. Find out why Firefox OS matters, how Mozilla builds community, and how you can contribute to Firefox.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37078", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37078", "speakers": [120025], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37085, "name": "Morning Yoga", "event_type": "Event", - + "time_start": "2014-07-22 07:30:00", "time_stop": "2014-07-22 08:15:00", "venue_serial": 1574, "description": "Programmers do a lot of sitting, so come refresh your body, mind, and spirit before you head into the day\u2019s sessions. This will be an easy beginner\u2019s yoga session \u2013 so don\u2019t be shy about coming out even if this will be your first yoga experience. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37085", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37085", "speakers": [], "categories": [ - + "Events" - + ] }, - + { "serial": 37086, "name": "Morning Yoga", "event_type": "Event", - + "time_start": "2014-07-23 07:30:00", "time_stop": "2014-07-23 08:15:00", "venue_serial": 1574, "description": "Programmers do a lot of sitting, so come refresh your body, mind, and spirit before you head into the day\u2019s sessions. This will be an easy beginner\u2019s yoga session \u2013 so don\u2019t be shy about coming out even if this will be your first yoga experience. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37086", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37086", "speakers": [], "categories": [ - + "Events" - + ] }, - + { "serial": 37087, "name": "Office Hour with Emma Jane Westby (Freelance)", "event_type": "Office Hours", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1548, "description": "Git can be frustrating and opaque. Don\u2019t worry, it\u2019s not you. It\u2019s Git. Come ask questions about making Git work for you instead of feeling stuck in a detached HEAD state. Emma Jane will talk about upgrading from a centralized system to a distributed workflow and improving team efficiencies with the right branching strategy. She\u2019ll also provide tips for teaching anyone how to use Git.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37087", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37087", "speakers": [4146], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37090, "name": "Office Hour with Ken Walker (IBM Canada) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1548, "description": "Need some advice on your next great open source project? Ken can steer you in the right direction. Talk to him about the benefits of the Eclipse Foundation as a home for your project, and discuss what prevents you from trying out cloud IDEs. Ken will also point out which open source components from Orion\u2019s JavaScript libraries you can leverage in your own project. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37090", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37090", "speakers": [39928], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37091, "name": "Office Hour with John Griffith (SolidFire) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1579, "description": "If you\u2019re looking for answers about OpenStack, John is ready to field your questions. He\u2019ll chat with you about block storage in OpenStack with Cinder, core OpenStack features and functionality, and general queries about OpenStack in general.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37091", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37091", "speakers": [171381], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37095, "name": "Office Hour with Damian Conway (Thoughtstream) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1548, "description": "The professor is in. Perl\u2019s own Dr. Evil emerges from his secret lair to talk about Perl 6, Perl 5, Vim, and presentation skills. Damian knows a lot about all of the above as the widely sought-after speaker who runs Thoughtstream, an international provider of programmer training. He devotes much of his spare time to Larry Wall and the design of Perl 6.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37095", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37095", "speakers": [4710], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37097, "name": "Office Hour with Adam Culp (Zend Technologies)", "event_type": "Office Hours", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1579, "description": "Organizer of the Sunshine PHP Developer Conference, Adam is ready to hold an open discussion on efficient code refactoring. Find out when to refactor, the key indicators for triggering a refactor, how to get started with code refactoring, and anything else you think is related.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37097", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37097", "speakers": [169862], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37098, "name": "Office Hour with Paco Nathan (Liber 118)", "event_type": "Office Hours", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1547, "description": ".", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37098", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37098", "speakers": [146540], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37099, "name": "Office Hour with Luciano Ramalho (Python.pro.br) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1548, "description": "Luciano is busy at OSCON with two sessions and a tutorial, but he\u2019s happy to answer any questions you may have about those talks: \u201cIntroduction to Python Metaprogramming\u201d, \u201cIdiomatic APIs with the Python Data Model\u201d, and \u201cPython: Encapsulation with Descriptors\u201d. The common theme for all three is how to use the special methods defined in the Python Data Model. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37099", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37099", "speakers": [150170], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37101, "name": "Office Hour with Jess Portnoy (Kaltura Inc) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1579, "description": "Join Jess to talk about useful debugging tools, PHP-based apps, and some of her other favorite open source topics. She\u2019ll hold forth on managing open source projects as a commercial company, deploying and maintaining large-scale video applications in the cloud, and open source development in general.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37101", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37101", "speakers": [171078], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37105, "name": "Office Hour with Michael Minella (Pivotal) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1547, "description": ".", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37105", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37105", "speakers": [171147], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37108, "name": "Office Hour with Wade Minter (TeamSnap) and Andrew Berkowitz (TeamSnap)", "event_type": "Office Hours", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1579, "description": "If you have questions about using applied improvisation for better communication, idea generation, and decision making in your company or team, come to Wade and Andrew. They\u2019ll talk to you about growing a distributed engineering team, and building and maintaining culture in a distributed organization. They\u2019ll also share tools and tips for improving communication.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37108", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37108", "speakers": [4378,106355], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37114, "name": "Office Hour with Tim Berglund (DataStax) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1579, "description": "Do you have questions on Apache Cassandra? Bring any and all of them to Tim during Office Hours. He\u2019s also ready to chat about technical training, the technologies formerly known as Big Data, and any other topics of mutual interest.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37114", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37114", "speakers": [137697], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37122, "name": "Office Hour with Joshua Marinacci (Nokia) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1548, "description": "Joshua is available to talk to you about any of his favorite topics, such as HTML Canvas, Bluetooth Low Energy, wearable computing, and the Internet of Things. And he\u2019s happy just to rant about Java.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37122", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37122", "speakers": [6931], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37129, "name": "Office Hour with Zach Supalla (Spark Labs) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 15:20:00", "time_stop": "2014-07-22 16:00:00", "venue_serial": 1547, "description": "Now that it is possible for startups to build products as powerful and game changing as Nest\u2014without millions of dollars\u2014how can you get in on the action? Zach Supalla from the Spark Labs startup accelerator will share details and answer questions about developing an IoT product, building a business on open source hardware, and growing from a Kickstarter campaign to a business.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37129", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37129", "speakers": [175040], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37134, "name": "Open Health Data/Content", "event_type": "BoF", - + "time_start": "2014-07-20 19:00:00", "time_stop": "2014-07-20 20:00:00", "venue_serial": 1458, "description": "Explore thoughts and ideas around public health and open data.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37134", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37134", "speakers": [], "categories": [ - + ] }, - + { "serial": 37144, "name": "Office Hour with Josh Berkus (PostgreSQL Experts, Inc.) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 15:20:00", "time_stop": "2014-07-22 16:00:00", "venue_serial": 1548, "description": "Josh Berkus is happy to chat with you about PostgreSQL high-availability and scale-out techniques, especially on cloud hosting. He\u2019ll discuss things like connection pooling and load-balancing tools, automated failover, RDS vs. roll-your-own on AWS, and scaling out multi-tenant apps.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37144", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37144", "speakers": [3397], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37148, "name": "Modernizing CS Education with Open Source", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1450, "description": "Scott Chacon, co-founder of GitHub, and Jay Borenstein, CS professor at Stanford and founder of Facebook's Open Academy, a program designed to match university students with open source projects for academic credit, will discuss how to bring the best of the open source community's learning frameworks into formal computer science education. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37148", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37148", "speakers": [837,180268], "categories": [ - + "Education" - + ] }, - + { "serial": 37153, "name": "Mandrill Party", "event_type": "Event", - + "time_start": "2014-07-23 20:30:00", "time_stop": "2014-07-23 22:00:00", "venue_serial": 1578, "description": "Join Mandrill for an OSCON Party at the Jupiter Hotel!", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37153", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37153", "speakers": [], "categories": [ - + "Events" - + ] }, - + { "serial": 37173, "name": "Office Hour with Andrei Alexandrescu (Facebook) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1547, "description": "Andrei will be happy to answer questions about the \u201cmove fast\u201d side of Facebook, including large-scale design, the D programming language, and Facebook\u2019s software engineering culture.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37173", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37173", "speakers": [109270], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37201, "name": "Office Hour with Brian Troutwine (AdRoll) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1579, "description": "In his talk (\u201cErlang, LFE, Joxa and Elixir: Established and Emerging Languages in the Erlang Ecosystem\u201d), Brian talks about the differences between these BEAM languages, and how each one is applicable to a distinct engineering task. During Office Hours, he\u2019ll tell you how to get started with each of these languages, and talk about the domains in which they succeed and fail.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37201", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37201", "speakers": [172990], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37253, "name": "Juju BoF", "event_type": "BoF", - + "time_start": "2014-07-22 19:00:00", "time_stop": "2014-07-22 20:00:00", "venue_serial": 1456, "description": "Deploying workloads in the cloud can be challenging, in this BoF we'll discuss how to use Ubuntu and Juju to get up and running in the cloud quickly ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37253", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37253", "speakers": [], "categories": [ - + ] }, - + { "serial": 37256, "name": "Office Hour with Michael Enescu (Cisco) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 15:20:00", "time_stop": "2014-07-23 16:00:00", "venue_serial": 1579, "description": "Now that you\u2019re familiar with cloud computing, along comes the next new thing: Fog computing. Michael will tell you what exactly Fog computing is, how it evolved from cloud compute, and where it\u2019s going. He\u2019ll also talk about network challenges in cloud computing with the Internet of Things, as well as IoT open source projects.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37256", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37256", "speakers": [172370], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37259, "name": "Modernizing your Cloud with Software Collections", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1461, "description": "Software Collections is a new way to run newer packages on Enterprise Linux (RHEL/CentOS) such as Python, Ruby, PHP, MySQL/MariaDB, and others. Learn how this enables us to use Perl 5.16 and PostgreSQL 9.2 alongside distribution-provided versions (on EL6, Perl 5.10, and PostgreSQL 8.4.). Also learn how we've extended the Perl 5.16 collection with additional packages.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37259", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37259", "speakers": [180437], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37292, "name": "Office Hour with Deb Nicholson (Open Invention Network)", "event_type": "Office Hours", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1580, "description": "Wondering how patents and copyright and trademark laws work with free and open source software projects? Deb is ready to share her knowledge. Find out if legislation and recent court decisions about patent trolls will keep you from being sued, and how the landscape likely to change. None of this is legal advice, just good solid background! ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37292", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37292", "speakers": [130731], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37306, "name": "Office Hour with Gian Merlino (Metamarkets) and Fangjin Yang (Metamarkets)", "event_type": "Office Hours", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1548, "description": "Gian and Fangjin will talk to you about real-time analytics with open source technologies, including the motivation for the Kafka, Storm, Hadoop, and Druid real-time analytics stack. They\u2019ll discuss implementation details (if you\u2019re interested in trying the stack out at home) and show you how to scale the stack and make it highly available. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37306", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37306", "speakers": [153566,153565], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37310, "name": "acilos, your Personal Social Valet", "event_type": "BoF", - + "time_start": "2014-07-22 19:00:00", "time_stop": "2014-07-22 20:00:00", "venue_serial": 1465, "description": "In this session, you will discover a new Open Source Personal Social Media Aggregation system called acilos. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37310", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37310", "speakers": [], "categories": [ - + ] }, - + { "serial": 37311, "name": "Open Standards vs. Open Source", "event_type": "BoF", - + "time_start": "2014-07-22 19:00:00", "time_stop": "2014-07-22 20:00:00", "venue_serial": 1463, "description": "There is a debate on the relevance of Open Standards when faced with Open Source efforts. Open Source is an industry force, yet government bodies and others still rely on and give preference to ANSI and ISO standards. Standards representatives from across the industry will be on hand to discuss possible paths forward that are complementary rather than competitive.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37311", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37311", "speakers": [], "categories": [ - + ] }, - + { "serial": 37316, "name": "Office Hour with Spencer Krum (HP) and William Van Hevelingen (Portland State University)", "event_type": "Office Hours", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1579, "description": "Spencer and William are ready to talk about all things Puppet related. They\u2019ll answer questions about module best practices and testing, using Hiera to manage data, and interfacing with the community. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37316", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37316", "speakers": [125113,99817], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37338, "name": "Office Hour with Drasko DRASKOVIC (DEVIALET) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1579, "description": "If you want to explore that thin line where hardware meets software, stop by and chat with Drasko. He\u2019ll talk to you about electronic prototyping of connected objects for the Internet of Things from a designer\u2019s point of view; discuss open source HW schematic modifications, component sourcing, and production; and weigh in on monitoring, data harvesting, remote control, sensors, and actuators.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37338", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37338", "speakers": [173105], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37345, "name": "Office Hour with Rob Reilly (Rob Reilly Consulting) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1580, "description": "When it comes to hacking micro-controller and nano-computing projects, Rob is your man. He\u2019s ready to discuss all your hacker questions, such as the latest micro-controller trends, the hacker cross-pollination with artists, scientists, engineers, students, and suits, and\u2014of course\u2014how to get started hacking and making.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37345", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37345", "speakers": [77350], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37363, "name": "Office Hour with Yoav Landman (JFrog) and Baruch Sadogursky (JFrog)", "event_type": "Office Hours", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1580, "description": "Talk to Yoav and Baruch about all things CI/CD, such as choosing the right build tools, and automating your build, deployment, and distribution process. They\u2019ll discuss the issues of moving to a cloud-based development stack, how to speed up development by dealing with bottlenecks in your CI/CD flow, and distributing software releases all the way to end users.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37363", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37363", "speakers": [116050,114822], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37401, "name": "Office Hour with Glen Wiley (Verisign, Inc.), Neel Goyal (Verisign, Inc.), Willem Toorop (NLNet Labs), and Allison Mankin (Verisign, Inc.) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 15:20:00", "time_stop": "2014-07-23 16:00:00", "venue_serial": 1580, "description": "Learn more about getdns for accessing DNS security and other powerful features. Targeted to application developers, getdns provides both basic and advanced DNS capabilities, while requiring little DNS expertise. Glen, Neel, Willem, and Allison will explain how to leverage DNS to create authenticated services with getdns modes. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37401", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37401", "speakers": [173324,172895,173325,173326], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37405, "name": "Office Hour with Dr. Doris Chen (Microsoft) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1580, "description": "Doris is ready to answer any questions you have about developing high performance websites and apps with JavaScript and HTML5. She\u2019ll share tips and tricks for tackling real-world web platform performance problems, including network requests, speed and responsiveness, and optimizing media usage.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37405", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37405", "speakers": [133360], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37408, "name": "Office Hour with Boyd Stephens (Netelysis, LLC)", "event_type": "Office Hours", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1579, "description": "Want to learn the intricacies of the money machine and where and when you can hack it? Boyd can help. Find out why staying in total control of your money machine/cash contraption can rival accepting venture capital and angel funding when creating a business. And learn how the Lean Business Model can open up unique opportunities for money machines. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37408", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37408", "speakers": [138530], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37409, "name": "Office Hour with Benjamin Curtis (Honeybadger Industries) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1580, "description": "If you want to find out more about machine learning, Benjamin is available to discuss several techniques, especially those available as Ruby gems. He\u2019ll talk to you about machine learning with Ruby, scaling Rails with PostgreSQL, and other questions you have on your mind. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37409", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37409", "speakers": [173396], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37410, "name": "Office Hour with David Quigley (KEYW Corporation) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1580, "description": "In his ongoing campaign to demystify SELinux, David is ready to tackle any questions you have\u2014including what it was like to work on the SELinux team at the NSA. He\u2019ll gladly talk to you about SELinux Internals and anything he covered (or didn\u2019t cover) in his tutorial, \u201cDemystifying SELinux Part II: Who\u2019s Policy Is It Anyway?\u201d", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37410", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37410", "speakers": [151833], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37411, "name": "Office Hour with Niklas Nielsen (Mesosphere, Inc.), Connor Doyle (Mesosphere, Inc.), and Adam Bordelon (Mesosphere, Inc.)", "event_type": "Office Hours", - + "time_start": "2014-07-22 15:20:00", "time_stop": "2014-07-22 16:00:00", "venue_serial": 1579, "description": "Niklas, Connor, and Adam are ready to talk to you about the Apache Mesos ecosystem for combining your datacenter servers and cloud instances into one shared pool. Find out how to take control of the datacenter with multi-tenancy and fault-tolerance, how to deploy and manage distributed applications in many languages, and how to migrate existing applications to Mesos using Marathon and Chronos.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37411", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37411", "speakers": [171598,172973,172898], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37412, "name": "Office Hour with Kelley Nielsen (Linux Foundation, Gnome Foundation) ", "event_type": "Office Hours", - + "time_start": "2014-07-23 14:30:00", "time_stop": "2014-07-23 15:10:00", "venue_serial": 1548, "description": "Learn how Kelley evolved from frustrated retail worker to Linux kernel developer through the Gnome Outreach Program for Women. She\u2019ll talk about the personal ways that outreach and mentoring affect new contributors\u2014and how important introspection and self-knowledge are in the process. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37412", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37412", "speakers": [140811], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37476, "name": "OpenStack Community Celebrates Four Years", "event_type": "Event", - + "time_start": "2014-07-22 19:00:00", "time_stop": "2014-07-22 20:30:00", "venue_serial": 1583, "description": "Celebrate OpenStack's Birthday\r\n\r\n ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37476", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37476", "speakers": [], "categories": [ - + "Events" - + ] }, - + { "serial": 37488, "name": "Current Best Practices for Building Enterprise Mobile Apps", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1460, "description": "With current best practices for mobile development, you can create great enterprise applications faster, iterate more often, and future-proof against a fast-changing mobile OS and hardware landscape. This session will look at key considerations for developers building enterprise apps for any device and OS.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37488", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37488", "speakers": [182198], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37566, "name": "Office Hour with Jason Swartz (Netflix, Inc.) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1580, "description": "Looking to get started with Scala? Jason is on hand to answer questions about this object-functional programming language. Find out how Netflix is using Scala for rapid API development, and why Scala may be useful to you. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37566", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37566", "speakers": [171226], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37567, "name": "Office Hour with Brian Bulkowski (Aerospike)", "event_type": "Office Hours", - + "time_start": "2014-07-22 15:20:00", "time_stop": "2014-07-22 16:00:00", "venue_serial": 1580, "description": "Stop by and chat with Brian about using the Aerospike database in real-time big data-driven applications. He\u2019ll talk to you about subjects including low latency, high throughput applications and cacheless architectures, what does and what doesn\u2019t work when using flash as a storage option, and big data management with Hadoop, Storm, Spark, and NoSQL.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37567", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37567", "speakers": [148534], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37578, "name": "Office Hour with Francesco Cesarini (Erlang Solutions Ltd), Robert Virding (Erlang Solutions Ltd.), and Marc Sugiyama (Erlang Solutions, Inc) ", "event_type": "Office Hours", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1580, "description": "Think you have the right use case to adopt Erlang or Elixir? Concerned about selling this new technology to management and operations? The Erlang Solutions crew will explain the advantages of the Erlang stack\u2014including when and when not to use it. Find out how to run an Erlang project, support and maintain Erlang clusters, and learn how to introduce Erlang to your organization.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37578", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37578", "speakers": [10595,174073,173421], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37581, "name": "Open Source Multiplies New Relic Awesomeness", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1463, "description": "New Relic believes in the principals of open source. So much so, that we built the New Relic Platform according those principals: by making the SDKs and our plugins open source, we ignited a rapidly growing and rich community of plugin authors. Come hear about our open source strategy, efforts in building community, and see how easy it is to participate.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37581", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37581", "speakers": [77939], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37583, "name": "Perl Web Development with CGI::Ex::App", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1460, "description": "CGI::Ex::App is a lightweight, high performance framework that has been quietly driving million-dollar websites since 2004. Come see why this application framework might be the perfect fit for you.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37583", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37583", "speakers": [137347], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37584, "name": "Welcome and Introduction", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 09:15:00", "venue_serial": 1584, "description": "Sarah Novotny, OSCON Program Chair, technical evangelist and community manager for NGINX will kick off Open Cloud Day. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37584", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37584", "speakers": [76338], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37585, "name": "How to Build Your Applications to Scale in the Cloud", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 09:15:00", "time_stop": "2014-07-21 09:45:00", "venue_serial": 1584, "description": "Whether you have one or a million visitors accessing your web app, they are all going to demand a great user experience regardless of what it takes for you to deliver it. This invariably means quick page loads and fast response times every single time.I am about different ways to start scaling your application with the new 'Cloud' technology.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37585", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37585", "speakers": [142320], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37586, "name": "Open Cloud APIs", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 11:00:00", "time_stop": "2014-07-21 11:30:00", "venue_serial": 1584, "description": "The success of development and deployment of anything is often unrelated to the technical superiority of a cloud platform but a product of what the platform enables you to do. We need robust but more importantly enjoyable to use APIs at every stage in an application's lifecycle. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37586", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37586", "speakers": [161475], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37588, "name": "Full Stack Development with Node, Angular, and Neo4j", "event_type": "BoF", - + "time_start": "2014-07-23 19:00:00", "time_stop": "2014-07-23 20:00:00", "venue_serial": 1454, "description": "Full stack is becoming a popular developer specialization for web and mobile application development. We'll be talking about the role of the full stack developer using open source platforms for building recommendation-based apps. We'll walk through full stack development on node.js, AngularJS, and Neo4j graph database.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37588", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37588", "speakers": [], "categories": [ - + ] }, - + { "serial": 37589, "name": "Office Hour with Steven Pousty (Red Hat) and Katie Miller (Red Hat)", "event_type": "Office Hours", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1580, "description": "Ready to write your components in the language of your choice, and put those components wherever you want on your network? Then come talk to Steve and Katie about the event-driven application framework Vert.X. They\u2019ll show you what the framework looks like and how you can get started with it.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37589", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37589", "speakers": [142320,175488], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37593, "name": "A Technical Exploration of Atom\u2019s Text Editor Component", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1461, "description": "Atom is an open-source desktop text editor built with web technology. This talk will be a deep, technical exploration of how Atom manipulates and renders text. Nathan will share lessons we\u2019ve learned about efficiently rendering text via the DOM, and then explore the key components involved in Atom\u2019s text editing system and how the concepts they model are surfaced in the API.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37593", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37593", "speakers": [2732], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37603, "name": "Dive into Cloud Foundry PaaS", "event_type": "BoF", - + "time_start": "2014-07-20 19:00:00", "time_stop": "2014-07-20 20:00:00", "venue_serial": 1471, "description": "Cloud Foundry is an open source Platform as a Service (PaaS) that features a range of components which provide a faster and easier way to build, test, deploy and scale applications. We will go through the most important elements and capabilities that make Cloud Foundry a first class citizen PaaS.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37603", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37603", "speakers": [], "categories": [ - + ] }, - + { "serial": 37604, "name": "Morning Break", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 10:45:00", "time_stop": "2014-07-21 11:00:00", "venue_serial": 1584, "description": "Located outside of F150 in the Lobby Foyer.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37604", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37604", "speakers": [], "categories": [ - + ] }, - + { "serial": 37605, "name": "Afternoon Break", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 15:00:00", "time_stop": "2014-07-21 15:15:00", "venue_serial": 1584, "description": "Located outside of F150 in the Lobby Foyer.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37605", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37605", "speakers": [], "categories": [ - + ] }, - + { "serial": 37606, "name": "Lunch", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 12:30:00", "time_stop": "2014-07-21 13:30:00", "venue_serial": 1584, "description": "Located in Exhibit Hall E. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37606", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37606", "speakers": [], "categories": [ - + ] }, - + { "serial": 37622, "name": "The Enterprise Challenge for Cloud Computing", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 09:45:00", "time_stop": "2014-07-21 10:15:00", "venue_serial": 1584, "description": "Clouds - they started as nice tools, became a favorite of\r\nstartups and Web 2.0 companies. But the enterprise, why hasn't cloud\r\ncomputing dominated in the enterprise already? Well, it's complicated...", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37622", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37622", "speakers": [141881], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37623, "name": "Real Time Backend for the Internet of Things", "event_type": "BoF", - + "time_start": "2014-07-22 20:00:00", "time_stop": "2014-07-22 21:00:00", "venue_serial": 1457, "description": "We will discuss the advantages and disadvantages of using SQL over NoSQL datastores, optimal solutions to the Blob problems, what \u201creal-time\u201d really means, dealing with sharding, spatial data types and the expected throughputs of such systems and more. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37623", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37623", "speakers": [], "categories": [ - + ] }, - + { "serial": 37624, "name": "Modern API Design via RAML, Swagger & Blueprints", "event_type": "BoF", - + "time_start": "2014-07-20 19:00:00", "time_stop": "2014-07-20 20:00:00", "venue_serial": 1453, "description": "Lets discuss how to iterate on APIs with our users, using their feedback to help us get the API right from the start. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37624", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37624", "speakers": [], "categories": [ - + ] }, - + { "serial": 37625, "name": "A Path to Achieving True Cloud Portability", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 14:30:00", "time_stop": "2014-07-21 15:00:00", "venue_serial": 1584, "description": "Moving applications from a traditional data center to a cloud\r\nprovides immediate and real advantages in the way services are delivered\r\nto customers. What if you could also easily move from one cloud to\r\nanother? ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37625", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37625", "speakers": [181484], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37626, "name": "The Art of Tizen UI Theme Technology in Various Profiles", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1460, "description": "Tizen is aimed at various profiles, not only mobile. The UI must be scalable and themeable to support these diverse profiles. This presentation will share the technology behind the scalable and themeable Tizen UI which is called EFL (Enlightenment Foundation Libraries). This will reduce development time tremendously to support multiple products and applications.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37626", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37626", "speakers": [181502], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37627, "name": "A New Community is Born in Internet of Things: Standard Specification to Open Source", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 11:20:00", "venue_serial": 1460, "description": "This talk includes an introduction to IoT and background, lessons learned from standard specification and its limitation, reason for open source in IoT, Samsung's efforts on IoT open source, and the future of IoT.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37627", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37627", "speakers": [172630], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37629, "name": "Why Open Platforms Matter to Enterprises and Developers", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1460, "description": "The industry needs cloud solutions built on an open, extensible architecture that delivers consistent access to infrastructure, runtimes, and application resources. As customers continue to adopt cloud service-based solutions, they need to avoid vendor lock-in, simplify building of complex cloud environments, and quickly develop cloud-ready applications that drive massively scalable cloud models.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37629", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37629", "speakers": [181598], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37631, "name": "A Deployment Architecture for OpenStack in the Enterprise", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 11:30:00", "time_stop": "2014-07-23 12:10:00", "venue_serial": 1463, "description": "Enterprise developers want flexible, open architectures to develop cloud-native applications and bring new ideas to market faster. Yet enterprise IT needs to quickly deliver services, applications, and infrastructures in a consistent, secure, repeatable manner. How do you get the agility and flexibility while maintaining control?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37631", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37631", "speakers": [26426], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37646, "name": "Storytelling on the Shoulders of Giants", "event_type": "Keynote", - + "time_start": "2014-07-24 09:40:00", "time_stop": "2014-07-24 09:50:00", "venue_serial": 1525, "description": "You may not feel like you\u2019re a \u201ccreative person,\u201d but never underestimate where your code could turn up or what stories it might tell. The most unassuming repo can be remixed into something magnificent. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37646", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37646", "speakers": [143674], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 37647, "name": "Open Platform for Family History", "event_type": "BoF", - + "time_start": "2014-07-22 20:00:00", "time_stop": "2014-07-22 21:00:00", "venue_serial": 1460, "description": "Meet to Learn, Define and Deliver Winning Products that Shape Tomorrow\r\n\r\nBillion of genealogical records available to read and write through open restful APIs. Learn what's available, how to use them, and get profiles of many application opportunities.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37647", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37647", "speakers": [], "categories": [ - + ] }, - + { "serial": 37648, "name": "Github RootsDev Javascript Open Source API Project", "event_type": "BoF", - + "time_start": "2014-07-23 19:00:00", "time_stop": "2014-07-23 20:00:00", "venue_serial": 1456, "description": "RootsDev has created a full function Javascript API that can be used to authenticate and read historical person data with in 30 minutes. Learn how and why this SDK was built and what open source apps are already talking to it.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37648", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37648", "speakers": [], "categories": [ - + ] }, - + { "serial": 37649, "name": "O'Reilly Author Book Signings - Tuesday", "event_type": "Event", - + "time_start": "2014-07-22 10:10:00", "time_stop": "2014-07-22 16:30:00", "venue_serial": 1597, "description": "Author book signings will be held in the O'Reilly Authors' booth on Tuesday and Wednesday. This is a great opportunity for you to meet O'Reilly authors and to get a free copy of their book. Complimentary copies will be provided for the first 25 attendees. Limit one free book per attendee.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37649", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37649", "speakers": [], "categories": [ - + ] }, - + { "serial": 37650, "name": "O'Reilly Author Book Signings - Wednesday", "event_type": "Event", - + "time_start": "2014-07-23 10:10:00", "time_stop": "2014-07-23 17:00:00", "venue_serial": 1597, "description": "Author book signings will be held in the O'Reilly Authors' booth on Tuesday and Wednesday. This is a great opportunity for you to meet O'Reilly authors and to get a free copy of their book. Complimentary copies will be provided for the first 25 attendees. Limit one free book per attendee.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37650", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37650", "speakers": [], "categories": [ - + ] }, - + { "serial": 37651, "name": "Office Hours - Tuesday", "event_type": "Event", - + "time_start": "2014-07-22 10:40:00", "time_stop": "2014-07-22 16:30:00", "venue_serial": 1596, "description": "Office Hours are your chance to meet face-to-face with OSCON presenters in a small-group setting. Drop in to discuss their sessions, ask questions, or make suggestions.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37651", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37651", "speakers": [], "categories": [ - + ] }, - + { "serial": 37652, "name": "Office Hours - Wednesday", "event_type": "Event", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 17:00:00", "venue_serial": 1596, "description": "Office Hours are your chance to meet face-to-face with OSCON presenters in a small-group setting. Drop in to discuss their sessions, ask questions, or make suggestions.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37652", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37652", "speakers": [], "categories": [ - + ] }, - + { "serial": 37657, "name": "Ubuntu Server Deep Dive", "event_type": "BoF", - + "time_start": "2014-07-22 20:00:00", "time_stop": "2014-07-22 21:00:00", "venue_serial": 1461, "description": "The Ubuntu Server BoF - come to tell the Ubuntu Server Product and Project Managers what would you like to see next - and hear from us what's new for Cloud users and a comprehensive tour of our security features. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37657", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37657", "speakers": [], "categories": [ - + ] }, - + { "serial": 37658, "name": "Adventures in Hackademia: Bridging Open Source and Higher Education", "event_type": "BoF", - + "time_start": "2014-07-23 19:00:00", "time_stop": "2014-07-23 20:00:00", "venue_serial": 1457, "description": "RIT recently announced the first Academic Minor in Free/Open Source Software and Free Culture in the United States (http://boingboing.net/2014/03/06/get-a-wee-degree-in-free-from.html) This session will detail the engagement strategies, metrics of success and failure, and educational resources that made it possible. Patches welcome. Forks encouraged. Teacher, Learner, and Hacker friendly session.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37658", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37658", "speakers": [], "categories": [ - + ] }, - + { "serial": 37659, "name": "Evolution of the Apache CouchDB Development Community", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1460, "description": "The Apache CouchDB project and world of open source development has seen a lot of change since 2005. In this talk, Joan Touzet will discuss the change journey the CouchDB community has taken as it has matured, covering advocacy efforts, Bylaws and Code of Conduct, GitHub, marketing efforts, mailing lists, growth of the committer base, and operating within the larger ASF community.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37659", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37659", "speakers": [181972], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37661, "name": "Driving Innovation and Next Generation Application Architectures with Open Source", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1461, "description": "Join Rackspace and CoreOS as they discuss/examine that developers are beginning to see a new set of disruptive technologies come into play - beyond virtual machine, beyond just configuration management.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37661", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37661", "speakers": [173503,30412], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37662, "name": "Author Book Signing with Paris Buttfield-Addison, Jonathon Manning, and Tim Nugent", "event_type": "Author Signing", - + "time_start": "2014-07-22 15:10:00", "time_stop": "2014-07-22 15:40:00", "venue_serial": 1598, "description": "Paris, Jon, and Tim will be at the O'Reilly Authors booth #719, signing copies of their book, Learning Cocoa with Objective-C, 4th Edition.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37662", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37662", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37663, "name": "Author Book Signing with Paco Nathan", "event_type": "Author Signing", - + "time_start": "2014-07-22 18:30:00", "time_stop": "2014-07-22 19:00:00", "venue_serial": 1549, "description": "Paco will be at the O'Reilly Authors booth #719, signing copies of his book, Enterprise Data Workflows with Cascading.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37663", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37663", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37664, "name": "Author Book Signing with Harry Percival", "event_type": "Author Signing", - + "time_start": "2014-07-22 10:10:00", "time_stop": "2014-07-22 10:40:00", "venue_serial": 1549, "description": "Harry will be at the O'Reilly Authors booth #719, signing copies of his book, Test-Driven Development with Python.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37664", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37664", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37665, "name": "Author Book Signing with Mike Amundsen", "event_type": "Author Signing", - + "time_start": "2014-07-22 15:40:00", "time_stop": "2014-07-22 16:10:00", "venue_serial": 1598, "description": "Mike will be at the O'Reilly Authors booth #719, signing copies of his book, RESTful Web APIs.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37665", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37665", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37666, "name": "Author Book Signing with Arun Gupta", "event_type": "Author Signing", - + "time_start": "2014-07-22 15:10:00", "time_stop": "2014-07-22 15:40:00", "venue_serial": 1549, "description": "Arun will be at the O'Reilly Authors booth #719, signing copies of his book, Java EE 7 Essentials.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37666", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37666", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37667, "name": "Author Book Signing with Anil Madhavapeddy", "event_type": "Author Signing", - + "time_start": "2014-07-22 10:10:00", "time_stop": "2014-07-22 10:40:00", "venue_serial": 1550, "description": "Anil will be at the O'Reilly Authors booth #719, signing copies of his book, Real World OCaml.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37667", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37667", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37668, "name": "Author Book Signing with Lorna Jane Mitchell", "event_type": "Author Signing", - + "time_start": "2014-07-23 10:10:00", "time_stop": "2014-07-23 10:40:00", "venue_serial": 1549, "description": "Lorna will be at the O'Reilly Authors booth #719, signing copies of her book, PHP Web Services.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37668", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37668", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37669, "name": "Author Book Signing with Kyle Simpson", "event_type": "Author Signing", - + "time_start": "2014-07-23 15:10:00", "time_stop": "2014-07-23 15:40:00", "venue_serial": 1549, "description": "Kyle will be at the O'Reilly Authors booth #719, signing copies of his book, You Don't Know JS: Scope & Closures.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37669", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37669", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37670, "name": "Author Book Signing with Ruth Suehle", "event_type": "Author Signing", - + "time_start": "2014-07-22 15:40:00", "time_stop": "2014-07-22 16:10:00", "venue_serial": 1549, "description": "Ruth will be at the O'Reilly Authors booth #719, signing copies of her book, Raspberry Pi Hacks.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37670", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37670", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37671, "name": "Author Book Signing with Mike Wolfson", "event_type": "Author Signing", - + "time_start": "2014-07-22 17:40:00", "time_stop": "2014-07-22 18:00:00", "venue_serial": 1549, "description": "Mike will be at the O'Reilly Authors booth #719, signing copies of his book, Programming the Android Developer Tools Essentials.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37671", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37671", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37672, "name": "Author Book Signing with Neal Ford", "event_type": "Author Signing", - + "time_start": "2014-07-22 15:10:00", "time_stop": "2014-07-22 15:40:00", "venue_serial": 1550, "description": "Neal will be at the O'Reilly Authors booth #719, signing copies of his book, Functional Thinking.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37672", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37672", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37673, "name": "Author Book Signing with Francesco Cesarini", "event_type": "Author Signing", - + "time_start": "2014-07-22 17:40:00", "time_stop": "2014-07-22 18:00:00", "venue_serial": 1550, "description": "Francesco will be at the O'Reilly Authors booth #719, signing copies of his book, Designing for Scalability with Erlang/OTP.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37673", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37673", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37674, "name": "Scalable, Fault Tolerant, Never-stop Systems: The World of Erlang", "event_type": "BoF", - + "time_start": "2014-07-23 19:00:00", "time_stop": "2014-07-23 20:00:00", "venue_serial": 1458, "description": "Join us for lighting talks and discussion of how to approach the hard problems of building scalable, fault tolerant systems in the real world using Erlang.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37674", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37674", "speakers": [], "categories": [ - + ] }, - + { "serial": 37675, "name": "Author Book Signing with Sebastien Goasguen", "event_type": "Author Signing", - + "time_start": "2014-07-22 18:00:00", "time_stop": "2014-07-22 18:30:00", "venue_serial": 1549, "description": "Sebastien will be at the O'Reilly Authors booth #719, signing copies of his book, 60 Recipes for Apache CloudStack.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37675", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37675", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37676, "name": "Author Book Signing with Dan Sanderson", "event_type": "Author Signing", - + "time_start": "2014-07-23 15:10:00", "time_stop": "2014-07-23 15:40:00", "venue_serial": 1550, "description": "Dan will be at the O'Reilly Authors booth #719, signing copies of his books, Programming Google App Engine with Java and Programming Google App Engine with Python.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37676", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37676", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37677, "name": "Author Book Signing with Alasdair Allan", "event_type": "Author Signing", - + "time_start": "2014-07-22 15:40:00", "time_stop": "2014-07-22 16:10:00", "venue_serial": 1550, "description": "Alasdair will be at the O'Reilly Authors booth #719, signing copies of his book, Distributed Network Data.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37677", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37677", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37678, "name": "What's a 'DPDK', and Where Can I Get One?", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 10:40:00", "time_stop": "2014-07-23 11:20:00", "venue_serial": 1463, "description": "Legend tells of a legendary piece of software whose packet processing speed is the stuff of legend. Find out all about this software, how it's made, where you can get it, and how it can help getting network packets into your native or virtualized application.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37678", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37678", "speakers": [182043], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37679, "name": "Author Book Signing with Steven Pousty and Katie Miller", "event_type": "Author Signing", - + "time_start": "2014-07-22 18:00:00", "time_stop": "2014-07-22 18:30:00", "venue_serial": 1550, "description": "Steven and Katie will be at the O'Reilly Authors booth #719, signing copies of their book, Getting Started with OpenShift.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37679", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37679", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37680, "name": "Author Book Signing with Scott Murray", "event_type": "Author Signing", - + "time_start": "2014-07-23 15:10:00", "time_stop": "2014-07-23 15:40:00", "venue_serial": 1598, "description": "Scott will be at the O'Reilly Authors booth #719, signing copies of his book, Interactive Data Visualization for the Web.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37680", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37680", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37681, "name": "Author Book Signing with Jason Strimpel", "event_type": "Author Signing", - + "time_start": "2014-07-23 15:40:00", "time_stop": "2014-07-23 16:10:00", "venue_serial": 1549, "description": "Jason will be at the O'Reilly Authors booth #719, signing copies of his book, Web Components.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37681", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37681", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37682, "name": "Author Book Signing with Jamie Allen", "event_type": "Author Signing", - + "time_start": "2014-07-23 13:30:00", "time_stop": "2014-07-23 14:00:00", "venue_serial": 1549, "description": "Jamie will be at the O'Reilly Authors booth #719, signing copies of his book, Effective Akka.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37682", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37682", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37683, "name": "Author Book Signing with Ethan Brown", "event_type": "Author Signing", - + "time_start": "2014-07-23 10:10:00", "time_stop": "2014-07-23 10:40:00", "venue_serial": 1550, "description": "Ethan will be at the O'Reilly Authors booth #719, signing copies of his book, Web Development with Node and Express.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37683", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37683", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37684, "name": "Author Book Signing with Tom Fifield and Everett Toews", "event_type": "Author Signing", - + "time_start": "2014-07-23 15:40:00", "time_stop": "2014-07-23 16:10:00", "venue_serial": 1598, "description": "Tom and Everett will be at the O'Reilly Authors booth #719, signing copies of their book, OpenStack Operations Guide.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37684", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37684", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37685, "name": "Author Book Signing with Jeff Sheltren and Narayan Newton", "event_type": "Author Signing", - + "time_start": "2014-07-23 10:10:00", "time_stop": "2014-07-23 10:40:00", "venue_serial": 1598, "description": "Jeff and Narayan will be at the O'Reilly Authors booth #719, signing copies of their book, High Performance Drupal.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37685", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37685", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37688, "name": "Containermania: The Hype, The Reality, and The Future", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 16:45:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1584, "description": "For this presentation, we'll take a look at the current state of Docker\r\ncontainers, what they're useful for, and where it's going.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37688", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37688", "speakers": [6845], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37689, "name": "The Human Element of APIs", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 12:00:00", "time_stop": "2014-07-21 12:30:00", "venue_serial": 1584, "description": "This talk will cover the interactions and influences that people and\r\ncommunities have over open source software and with one another. Special\r\nattention will be given to many of the hot technologies being used in\r\nand for Open Cloud technologies.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37689", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37689", "speakers": [182080], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37690, "name": "How Disney Built a Modern Cloud with Open Source", "event_type": "Open Cloud Day", - + "time_start": "2014-07-21 10:15:00", "time_stop": "2014-07-21 10:45:00", "venue_serial": 1584, "description": "Working to help reboot the delivery and consumption of\r\ntechnology services within the Walt Disney Company, we've built a cloud\r\nservice offering and integration platform on open source technologies like\r\nOpenStack with the goal of providing ways to host any application -\r\nwhether modern and cloud ready or on software development hospice care.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37690", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37690", "speakers": [182081], "categories": [ - + "Cloud" - + ] }, - + { "serial": 37698, "name": "Designers, See Your Designs Differently: Color Contrast Tips and Techniques", "event_type": "BoF", - + "time_start": "2014-07-23 19:00:00", "time_stop": "2014-07-23 20:00:00", "venue_serial": 1461, "description": "This session will cover the strategies, tips and techniques we have used at PayPal with our design teams to move towards a goal of delivering products with sufficient color contrast for all user experiences to assure that as many people as possible can see and use them. Designers and developers are welcome to attend. \r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37698", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37698", "speakers": [], "categories": [ - + ] }, - + { "serial": 37699, "name": "Glimpse of Git's Future", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 16:10:00", "time_stop": "2014-07-22 16:50:00", "venue_serial": 1454, "description": "Peek into the future of Git with Android, Google and GitHub. Learn\r\nabout the 450x server performance improvement developed by Google and\r\nGitHub, and get a glimpse of the scaling roadmap.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37699", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37699", "speakers": [64512], "categories": [ - + "Tools & Techniques" - + ] }, - + { "serial": 37703, "name": "Alchemy.js", "event_type": "BoF", - + "time_start": "2014-07-20 19:00:00", "time_stop": "2014-07-20 20:00:00", "venue_serial": 1470, "description": "Alchemy.js is a new open source visualization library from GraphAlchemist. Come join the team behind the new tool and learn about the benefits of using Alchemy.js.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37703", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37703", "speakers": [], "categories": [ - + ] }, - + { "serial": 37704, "name": "Identity: Authentication and User Management", "event_type": "BoF", - + "time_start": "2014-07-23 19:00:00", "time_stop": "2014-07-23 20:00:00", "venue_serial": 1463, "description": "Acquiring users, getting them signed up, and protecting both your and their interests are all hard things. What are the hard parts, what are the available tools, and what are the OSS angles?", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37704", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37704", "speakers": [], "categories": [ - + ] }, - + { "serial": 37708, "name": "Taming Hybrid Clouds with ManageIQ ", "event_type": "BoF", - + "time_start": "2014-07-22 19:00:00", "time_stop": "2014-07-22 20:00:00", "venue_serial": 1460, "description": "ManageIQ is a newly open sourced project that lets operations and developers manage the lifecycle of their virtualization and cloud infrastructures. Place virtual workloads according to your policies and automate them, prioritizing for cost, performance, security, and/or reliability. In this BoF, we will demonstrate how to use ManageIQ as a single API and gateway for cloud workloads.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37708", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37708", "speakers": [], "categories": [ - + ] }, - + { "serial": 37709, "name": "Leveraging a Cloud Architecture for Fun, Ease, and Profit", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 11:30:00", "time_stop": "2014-07-22 12:10:00", "venue_serial": 1463, "description": "The world of cloud and application development is not just for the hardened developer these days. In their session, Phil Jackson, Development Community Advocate for SoftLayer, and Harold Hannon, Sr. Software Architect at SoftLayer, will pull back the curtain of the architecture of a fun demo application purpose-built for the cloud.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37709", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37709", "speakers": [140339,122564], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37712, "name": "Gluster Community BoF", "event_type": "BoF", - + "time_start": "2014-07-22 19:00:00", "time_stop": "2014-07-22 20:00:00", "venue_serial": 1458, "description": "Get up to speed with the Gluster Community - we released 3.5, planned 3.6, created lots of language bindings for GFAPI, and are currently planning the Gluster Software Distribution. Come hear about the latest developments in this BoF and how you can participate and benefit.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37712", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37712", "speakers": [], "categories": [ - + ] }, - + { "serial": 37713, "name": "A Hands-on Intro to Data Science and R", "event_type": "Event", - + "time_start": "2014-07-21 09:00:00", "time_stop": "2014-07-21 12:30:00", "venue_serial": 1607, "description": "Data scientists need to have a grab bag of tools available to accomplish the task of value-driven data analytics. Many of those tools are open source. Come see how Pivotal is leveraging and contributing to open source with data science. Special focus on: R, Python, MADlib, Open Chorus, Apache Tomcat, Apache Hadoop, Redis, Rabbit MQ, Cloud Foundry and other open source toolkits. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37713", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37713", "speakers": [182463], "categories": [ - + "Sponsored Tutorials" - + ] }, - + { "serial": 37714, "name": "Author Signing with Miguel Grinberg", "event_type": "Author Signing", - + "time_start": "2014-07-22 10:10:00", "time_stop": "2014-07-22 10:40:00", "venue_serial": 1598, "description": "Miguel will be at the O'Reilly Authors booth #719, signing copies of his book, Flask Web Development.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37714", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37714", "speakers": [], "categories": [ - + "Author Signings" - + ] }, - + { "serial": 37715, "name": "TA3M", "event_type": "BoF", - + "time_start": "2014-07-22 19:00:00", "time_stop": "2014-07-22 20:00:00", "venue_serial": 1466, "description": "Leaders in technology convene to discuss the 'state of the movement' for issues around privacy, censorship, and surveillance. What are our successes and failures? What are the current threats and opportunities to affect meaningful change? ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37715", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37715", "speakers": [], "categories": [ - + ] }, - + { "serial": 37717, "name": "CLA\u2019s: Best Thing Since Sliced Bread or Tool of the Devil\u2026 A Panel Discussion", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 16:10:00", "time_stop": "2014-07-23 16:50:00", "venue_serial": 1462, "description": "Open Source licenses are mostly grounded in US Copyright Law, which requires 51% representation to claim standing in any copyright-related action (including defense against infringement claims as well as re-licensing). Yet, they are also a barrier to participation, since you often must have one in place before you make a substantial (or in some cases any) contribution.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37717", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37717", "speakers": [179595,7969,46421,6380], "categories": [ - + "Business" - + ] }, - + { "serial": 37719, "name": "Making a Difference through Open Source", "event_type": "Keynote", - + "time_start": "2014-07-22 09:20:00", "time_stop": "2014-07-22 09:30:00", "venue_serial": 1525, "description": "Non-profit entities help impact the lives for millions of people, and make the world a better place. Bluehost is working with Grassroots.org to help make it simpler for non-profits to get online and share their messages with the world. Join us and learn how you can help make a difference.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37719", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37719", "speakers": [3476,181009,182521], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 37721, "name": "10 Years of Google Summer of Code", "event_type": "Keynote", - + "time_start": "2014-07-22 09:50:00", "time_stop": "2014-07-22 09:55:00", "venue_serial": 1525, "description": "A (very) fast overview of the results of the program: lines of code, #'s of participants, and so on. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37721", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37721", "speakers": [81759], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 37723, "name": "Bringing OpenStack based Cloud to the Enterprise", "event_type": "Keynote", - + "time_start": "2014-07-22 09:45:00", "time_stop": "2014-07-22 09:50:00", "venue_serial": 1525, "description": "More and more Enterprises are evaluating and adopting OpenStack as an option for deployments in the cloud. HP Helion OpenStack is\r\nthe latest addition to the OpenStack distribution, find out how your Enterprise can leverage it to deliver a scalable, secure and stable cloud environment for complex workloads.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37723", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37723", "speakers": [177325], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 37724, "name": "High Performance Visualizations with Canvas", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1460, "description": "This session will dig into the nuts and bolts of Canvas and explore techniques for implementing beautiful, smooth, and efficient visualizations.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37724", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37724", "speakers": [160033], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37733, "name": "Comics for Communication and Fun!", "event_type": "BoF", - + "time_start": "2014-07-23 19:00:00", "time_stop": "2014-07-23 20:00:00", "venue_serial": 1465, "description": "Learn how to draw comics or practice drawing with fellow cartoonists at this fun session. We're all friends, regardless of skill level!", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37733", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37733", "speakers": [], "categories": [ - + ] }, - + { "serial": 37736, "name": "Apache Spark: A Killer or Savior of Apache Hadoop?", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1463, "description": "The Big Boss(tm) has just OKed the first Hadoop cluster in the company. You are the guy in charge of analyzing petabytes of your company's valuable data using a combination of custom MapReduce jobs and SQL-on-Hadoop solutions. All of a sudden the web is full of articles telling you that Hadoop is dead, Spark has won and you should quit while you're still ahead. But should you? ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37736", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37736", "speakers": [151691], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37738, "name": "Open Manufacturing: Bringing Open Hardware Beyond 3D Printing", "event_type": "Keynote", - + "time_start": "2014-07-24 09:30:00", "time_stop": "2014-07-24 09:40:00", "venue_serial": 1525, "description": "Open source design has been a recent trend in hardware, but it tends to be limited to open libraries of 3D-printable parts. These are geared at makers, artists, hobbyists, and whoever else really wants to print their own figurines, not necessarily the engineering community.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37738", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37738", "speakers": [182587], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 37742, "name": "OpenBTS: Develop Your Own Cell Network & Apps!", "event_type": "BoF", - + "time_start": "2014-07-23 19:00:00", "time_stop": "2014-07-23 20:00:00", "venue_serial": 1466, "description": "Learn how to build your own cell network and invent cell network applications and mobile services using OpenBTS APIs in a session led by Harvind Samra, co-founder of the OpenBTS project, and Michael Iedema, Senior Engineer at Range Networks.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37742", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37742", "speakers": [], "categories": [ - + ] }, - + { "serial": 37744, "name": "Office Hour with Renee DiResta (OATV), Bryce Roberts (OATV), and Roger Chen (OATV)", "event_type": "Office Hours", - + "time_start": "2014-07-22 13:00:00", "time_stop": "2014-07-22 13:30:00", "venue_serial": 1546, "description": "Got an Open Source project you\u2019re thinking of turning into a startup? Got a startup you\u2019re planning to raise funding for? Want help or feedback on your business plan? O\u2019Reilly AlphaTech Ventures is at OSCON and here to help. Come by and say hi!", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37744", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37744", "speakers": [122516,1402,171704], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37755, "name": "Cassandra 2.X: Transactions, NoSQL, and Performance", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1463, "description": "Cassandra 2.0 introduced lightweight transactions (LWT), making it the first database to allow mixing linearizable transactions into a fully distributed, highly availability system ('AP' in CAP terminology).\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37755", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37755", "speakers": [140062], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37759, "name": "Geek Choir", "event_type": "BoF", - + "time_start": "2014-07-23 20:00:00", "time_stop": "2014-07-23 21:00:00", "venue_serial": 1464, "description": "Much has been written about the connections between mathematics and music. Come meet and collaborate with your fellow attendees as we engage in another OSCON tradition: Geek Choir! (Yes, there will be singing.)", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37759", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37759", "speakers": [], "categories": [ - + ] }, - + { "serial": 37767, "name": "PostgreSQL BoF", "event_type": "BoF", - + "time_start": "2014-07-22 19:00:00", "time_stop": "2014-07-22 20:00:00", "venue_serial": 1451, "description": "Get together with the local users group + Pg folks in town for OSCON.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37767", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37767", "speakers": [], "categories": [ - + ] }, - + { "serial": 37768, "name": "Office Hour with Tim O'Reilly (O'Reilly Media, Inc.)", "event_type": "Office Hours", - + "time_start": "2014-07-22 12:45:00", "time_stop": "2014-07-22 13:15:00", "venue_serial": 1547, "description": "Have you wondered about \u201cbig idea marketing\u201d? That is, how to align what you do with big movements many people care about? Tim has advice on this and other topics, such as company culture, startup and project pitches, how programming is changing in the era of cloud computing and DevOps, and why it\u2019s important for coders to help improve government services (like the healthcare.gov rescue effort).", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37768", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37768", "speakers": [251], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37769, "name": "Office Hour with Tim Bray (Independent)", "event_type": "Office Hours", - + "time_start": "2014-07-22 17:45:00", "time_stop": "2014-07-22 18:15:00", "venue_serial": 1546, "description": "Are you worried about software, the Net, and life online? According to Tim, now is the time for sensible, reasonable, extreme paranoia. Come chat with him about privacy policy and technology, public-key encryption, identity federation, and related topics. ", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37769", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37769", "speakers": [24978], "categories": [ - + "Office Hours" - + ] }, - + { "serial": 37771, "name": "Tim O'Reilly", "event_type": "Keynote", - + "time_start": "2014-07-23 10:00:00", "time_stop": "2014-07-23 10:10:00", "venue_serial": 1525, "description": "Keynote by Tim O'Reilly, founder and CEO of O'Reilly Media.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37771", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37771", "speakers": [251], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 37773, "name": "Hello User Group", "event_type": "BoF", - + "time_start": "2014-07-23 19:00:00", "time_stop": "2014-07-23 20:00:00", "venue_serial": 1451, "description": "An open discussion on how to start a local user group and grow it into a successful community.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37773", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37773", "speakers": [], "categories": [ - + ] }, - + { "serial": 37775, "name": "Gophers of a Feather", "event_type": "BoF", - + "time_start": "2014-07-23 20:00:00", "time_stop": "2014-07-23 21:00:00", "venue_serial": 1454, "description": "Come meet other Go developers and hear why Go is becoming the new language of the cloud.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37775", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37775", "speakers": [], "categories": [ - + ] }, - + { "serial": 37776, "name": "Application Blueprints with Apache Brooklyn", "event_type": "BoF", - + "time_start": "2014-07-22 20:00:00", "time_stop": "2014-07-22 21:00:00", "venue_serial": 1456, "description": "brooklyn.io lets you create blueprints to deploy and manage distributed applications, building on best-practice blueprints and policies maintained by the community. Recently accepted into the Apache Incubator, Brooklyn is a multi-cloud autonomic control plane based on the OASIS CAMP YAML standard for composing deployment plans.\r\n\r\nThis BoF is for anyone wanting to share experiences or learn more.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37776", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37776", "speakers": [], "categories": [ - + ] }, - + { "serial": 37777, "name": "Managing Containerized Applications", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1460, "description": "Containers provide an opportunity for more direct management of\r\napplications. However, loosely coupled, distributed, elastic\r\nmicro-services require more than individual containers and hosts.\r\nKubernetes is a new open source project inspired by Google\u2019s internal\r\nworkload management systems that establishes robust primitives for\r\nmanaging applications comprised of multiple containers.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37777", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37777", "speakers": [183169], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37778, "name": "Bringing More Women to Free and Open Source Software", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 17:00:00", "time_stop": "2014-07-22 17:40:00", "venue_serial": 1463, "description": "This talk will introduce GNOME's Outreach Program for Women, outline\r\nhow the program works and update you on what's happened recently.\r\nTired of people talking about how there isn't diversity in free\r\nsoftware? Come to this talk and find out how you can help actually\r\nchange things.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37778", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37778", "speakers": [173364], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37779, "name": "The Epic Battle: Scala at PayPal", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 13:40:00", "time_stop": "2014-07-22 14:20:00", "venue_serial": 1463, "description": "There\u2019s only one way to do it here at PayPal \u2013 with a framework. Everything you'll ever need is done if you stay inside the lines. Imagine my reaction when I joined PayPal with Scala and high hopes. We have many reasons to avoid the monolithic framework, so we were going against the grain from day 1.\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37779", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37779", "speakers": [128980], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37780, "name": "DreamFactory REST API Platform for Mobile and IoT", "event_type": "BoF", - + "time_start": "2014-07-23 20:00:00", "time_stop": "2014-07-23 21:00:00", "venue_serial": 1458, "description": "DreamFactory is an open source REST API platform that makes it easy to develop mobile and IoT applications without rolling your own user management, security, and REST APIs on the server. Come learn what DreamFactory is all about!", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37780", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37780", "speakers": [], "categories": [ - + ] }, - + { "serial": 37794, "name": "Containers - What We Have and What's Missing?", "event_type": "BoF", - + "time_start": "2014-07-22 20:00:00", "time_stop": "2014-07-22 21:00:00", "venue_serial": 1463, "description": "We will discuss the current state of containers and what can/should be improved.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37794", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37794", "speakers": [], "categories": [ - + ] }, - + { "serial": 37795, "name": "Gophers with Hammers: Fun with Parsing and Generating Go", "event_type": "40-minute conference session", - + "time_start": "2014-07-22 14:30:00", "time_stop": "2014-07-22 15:10:00", "venue_serial": 1461, "description": "Go is an open source language first released in 2009. Go is popular\r\nbecause it makes coding super-fun again. Josh will illustrate the deep role tools\r\nlike gofmt, godoc, 'go vet', 'go test' and others play in the Go\r\nexperience, show you how to roll your own, and talk about some unexplored\r\npossibilities.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37795", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37795", "speakers": [179435], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37801, "name": "Racing Change: Accelerating Innovation Through Radical Transparency", "event_type": "Keynote", - + "time_start": "2014-07-23 09:20:00", "time_stop": "2014-07-23 09:30:00", "venue_serial": 1525, "description": "In February of this year, PayPal announced it had hired Danese Cooper as their first Head of Open Source. PayPal? And Open Source? In fact, Open Source is playing a key role in reinventing PayPal engineering as a place where innovation at scale is easy and fun - especially if you like to work in Open Source.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37801", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37801", "speakers": [76338,179599,179595,179435], "categories": [ - + "Keynotes" - + ] }, - + { "serial": 37808, "name": "Build Responsive Web Apps with OpenUI5", "event_type": "40-minute conference session", - + "time_start": "2014-07-23 13:40:00", "time_stop": "2014-07-23 14:20:00", "venue_serial": 1461, "description": "Meet OpenUI5--a powerful web UI library for developing responsive web apps that run on and adapt to any current browser and device. \r\n\r\n", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37808", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37808", "speakers": [173233,104828,170822], "categories": [ - + "Sponsored Sessions" - + ] }, - + { "serial": 37810, "name": "Gigawatts", "event_type": "Event", - + "time_start": "2014-07-23 19:00:00", "time_stop": "2014-07-23 19:30:00", "venue_serial": 1450, "description": "Some talks carefully guide the listeners through the entirety of a topic,\r\n starting with the basics and ending with the fine details.\r\n\r\nThat's\u2026 not the plan for this talk.", - "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37810", + "website_url": "http://oscon.com/oscon2014/public/schedule/detail/37810", "speakers": [3189], "categories": [ - + "Events", - + "Perl" - + ] }, - - + + { "serial": "slot_15176", "name": "Lunch", "event_type": "break", - + "time_start": "2014-07-20 12:30:00", "time_stop": "2014-07-20 13:30:00", "venue_serial": 1468, @@ -8571,12 +8571,12 @@ "Break" ] }, - + { "serial": "slot_15177", "name": "Lunch", "event_type": "break", - + "time_start": "2014-07-21 12:30:00", "time_stop": "2014-07-21 13:30:00", "venue_serial": 1468, @@ -8584,12 +8584,12 @@ "Break" ] }, - + { "serial": "slot_15276", "name": "Morning Break", "event_type": "break", - + "time_start": "2014-07-22 10:10:00", "time_stop": "2014-07-22 10:40:00", "venue_serial": 1467, @@ -8597,12 +8597,12 @@ "Break" ] }, - + { "serial": "slot_15277", "name": "Lunch", "event_type": "break", - + "time_start": "2014-07-22 12:10:00", "time_stop": "2014-07-22 13:40:00", "venue_serial": 1469, @@ -8610,12 +8610,12 @@ "Break" ] }, - + { "serial": "slot_15279", "name": "Afternoon break", "event_type": "break", - + "time_start": "2014-07-22 15:10:00", "time_stop": "2014-07-22 16:10:00", "venue_serial": 1467, @@ -8623,12 +8623,12 @@ "Break" ] }, - + { "serial": "slot_15294", "name": "Morning Break", "event_type": "break", - + "time_start": "2014-07-23 10:10:00", "time_stop": "2014-07-23 10:40:00", "venue_serial": 1467, @@ -8636,12 +8636,12 @@ "Break" ] }, - + { "serial": "slot_15295", "name": "Lunch", "event_type": "break", - + "time_start": "2014-07-23 12:10:00", "time_stop": "2014-07-23 13:40:00", "venue_serial": 1469, @@ -8649,12 +8649,12 @@ "Break" ] }, - + { "serial": "slot_15311", "name": "Afternoon Break", "event_type": "break", - + "time_start": "2014-07-23 15:10:00", "time_stop": "2014-07-23 16:10:00", "venue_serial": 1467, @@ -8662,12 +8662,12 @@ "Break" ] }, - + { "serial": "slot_15409", "name": "Morning Break", "event_type": "break", - + "time_start": "2014-07-24 10:40:00", "time_stop": "2014-07-24 11:00:00", "venue_serial": 1473, @@ -8675,12 +8675,12 @@ "Break" ] }, - + { "serial": "slot_15895", "name": "Dinner", "event_type": "break", - + "time_start": "2014-07-23 17:40:00", "time_stop": "2014-07-23 19:00:00", "venue_serial": 1523, @@ -8688,13 +8688,13 @@ "Break" ] } - + ], "speakers": [ - + { - + "serial": 149868, "name": "Faisal Abid", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_149868.jpg", @@ -8704,9 +8704,9 @@ "twitter": "FaisalAbid", "bio": "

I am a software engineer, author, teacher and entrepreneur.

\n

From the hardcore server-side and database challenges to the the front end issues, I love solving problems and strive to create software that can make a difference.

\n

I’m an author published by Manning and O’Reilly and have also appeared in leading publications with my articles on ColdFusion and Flex.

\n

In my free time I teach Android or Node.js at workshops around the word, speak at conferences such as OSCON, CodeMotion, FITC and AndroidTO.

\n

Currently, I’m the founder of Dynamatik, a design and development agency in Toronto.

\n

During the days I am a Software Engineer at Kobo working on OS and app level features for Android tablets.

" }, - + { - + "serial": 172532, "name": "Josh Adams", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172532.jpg", @@ -8716,9 +8716,9 @@ "twitter": "knewter", "bio": "

I’m the CTO of Isotope11, a mildly successful software development company that focuses on Ruby, JavaScript, and Erlang/Elixir. I’m also responsible for http://www.elixirsips.com, a screencast series wherein I walk through elixir as I learn it, and record 2 shortish (5-12 minutes, occasionally longer) videos per week. I’ve also been a co-author and a technical reviewer for multiple books on the Arduino microprocessor and Ruby, and have had a lot of fun doing robotics with Ruby as well. I’m currently aiming to bring that robotics fun to Erlang (or Elixir)

" }, - + { - + "serial": 104828, "name": "DJ Adams", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_104828.jpg", @@ -8728,9 +8728,9 @@ "twitter": "qmacro", "bio": "

DJ Adams is an enterprise architect and open source programmer, author, and bread-maker living in Manchester, working as a Principal Consultant for Bluefin Solutions. He has a degree in Latin & Greek (Classics) from the University of London, and despite having been referred to as an alpha geek, can nevertheless tie his own shoelaces and drink beer without spilling it.

\n

He has written two books for O’Reilly, on Jabber and on Google.

\n

He is married to his theoretical childhood sweetheart Michelle, and has a son, Joseph, of whom he is very proud.

" }, - + { - + "serial": 29558, "name": "Lance Albertson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_29558.jpg", @@ -8740,9 +8740,9 @@ "twitter": "ramereth", "bio": "

Lance Albertson is the Director for the Oregon State University Open Source Lab (OSL) and has been involved with the Gentoo Linux project as a developer and package maintainer since 2003. Since joining the OSL in 2007, Lance has managed all of the hosting activities that the OSL provides for nearly 160 high-profile open source projects. He was recently promoted to Director in early 2013 after being the Lead Systems Administration and Architect since 2007.

\n

Prior to joining the OSUOSL, Lance was a UNIX Administrator for the Enterprise Server Technologies group at Kansas State University. Lance prepared for life as a career systems administrator by grappling with natural systems first, joining his father near Hiawatha, Kansas on the family farm growing corn and soybeans.

\n

In his free time he helps organize the Corvallis Beer and Blog and plays trumpet in a local jazz group The Infallible Collective. He holds a B.A. in Agriculture Technology Management from Kansas State University, where he minored in Agronomy and Computer Science.

" }, - + { - + "serial": 109270, "name": "Andrei Alexandrescu", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_109270.jpg", @@ -8752,9 +8752,9 @@ "twitter": "incomputable", "bio": "

Andrei Alexandrescu is a Research Scientist at Facebook and coined the colloquial term ‘modern C++’, used today to describe a collection of important C++ styles and idioms. His eponymous book on the topic, Modern C++ Design (Addison-Wesley, 2001), revolutionized C++ programming and
\nproduced a lasting influence not only on subsequent work on C++, but also on other languages and systems. With Herb Sutter, Andrei is also the
\ncoauthor of C++ Coding Standards (Addison-Wesley, 2004).

\n

Through Andrei’s varied work on libraries and applications, as well as his research in
\nmachine learning and natural language processing, he has garnered a solid reputation in both industrial and academic circles. Andrei has also been
\nthe key designer of many important features of the D programming language and has authored a large part of D’s standard library, positioning him to
\nwrite an authoritative book on the new language, appropriately entitled The D Programming Language (Addison-Wesley, 2010).

\n

Andrei holds a Ph.D. in Computer Science from the University of Washington and a B.Sc. in Electrical Engineering from University ‘Politehnica’ Bucharest.

" }, - + { - + "serial": 2216, "name": "Alasdair Allan", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_2216.jpg", @@ -8764,9 +8764,9 @@ "twitter": "aallan", "bio": "

Alasdair Allan is a Scientist, Author, Hacker, Tinkerer and Journalist who has been thinking about the Internet of Things, which he thinks is broken.

\n

He is the author of a number of books, and from time to time he also stands in front of cameras. You can often find him at conferences talking about interesting things, or deploying sensors to measure them. He recently rolled out a mesh network of five hundred sensors motes covering the entire of Moscone West during Google I/O. He’s still recovering.

\n

A few years before that he caused a privacy scandal by uncovering that your iPhone was recording your location all the time. This caused several class action lawsuits and a U.S. Senate hearing. Several years on, he still isn’t sure what to think about that.

\n

He sporadically writes blog posts about things that interest him, or more frequently provides commentary in 140 characters or less. He is a contributing editor for MAKE magazine, and a contributor to the O’Reilly Radar.

\n

Alasdair is a former academic. As part of his work he built a distributed peer-to-peer network of telescopes which, acting autonomously, reactively scheduled observations of time-critical events. Notable successes included contributing to the detection of what was\u2014at the time\u2014the most distant object yet discovered, a gamma-ray burster at a redshift of 8.2.

" }, - + { - + "serial": 170293, "name": "Jamie Allen", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_170293.jpg", @@ -8776,9 +8776,9 @@ "twitter": "jamie_allen", "bio": "

Jamie Allen has worked in consulting since 1994, with top firms including Price Waterhouse and Chariot Solutions. He has a long track record of collaborating closely with clients to build high-quality, mission-critical systems that scale to meet the needs of their businesses, and has worked in myriad industries including automotive, retail, pharmaceuticals, telecommunications and more. Jamie has been coding in Scala and actor-based systems since 2009, and is the author of “Effective Akka” book from O’Reilly.

" }, - + { - + "serial": 46440, "name": "Rob Allen", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_46440.jpg", @@ -8788,9 +8788,9 @@ "twitter": "", "bio": "

Rob Allen is a software engineer, project manager and trainer. Rob Allen has been programming in a with PHP for a very long time now and contributes to Zend Framework and other open source projects. He is a ZF contributor, member of the ZF Community team and also wrote Zend Framework in Action. Rob holds a Masters degree in Electronic Engineering from the University of Birmingham in the UK and started out writing C++ applications; he now concentrates solely on web-based applications in PHP. Rob is UK-based and runs Nineteen Feet Limited, focussing on web development, training and consultancy.

" }, - + { - + "serial": 117513, "name": "Dan Allen", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_117513.jpg", @@ -8800,9 +8800,9 @@ "twitter": "mojavelinux", "bio": "

Dan is an open source advocate, community catalyst, software generalist, author and speaker. Most of the time, he’s hacking with some JVM language. He leads the Asciidoctor project and serves as the community liaison for Arquillian. He builds on these experiences to help make a variety of open source projects wildly successful, including Asciidoctor, Arquillian, Opal and JBoss Forge.

\n

Dan is the author of Seam in Action (Manning, 2008) and has written articles for NFJS, the Magazine, IBM developerWorks, Java Tech Journal and JAXenter. He’s also an internationally recognized speaker, having presented at major software conferences including JavaOne, Devoxx, OSCON, NFJS / UberConf / RWX, JAX and jFokus. He’s recognized as a JavaOne Rock Star and Java (JVM) Champion.

\n

After a long conference day, you’ll likely find Dan geeking out about technology, documentation and testing with fellow community members over a Trappist beer or Kentucky Bourbon.

" }, - + { - + "serial": 173281, "name": "Mohammad Almalkawi", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173281.jpg", @@ -8812,9 +8812,9 @@ "twitter": "moh", "bio": "

Mohammad is a software engineer at Quip. Before Quip, Mohammad was a technical lead on Twitter’s Mobile team where he was focused on Android platform and mobile tools. Prior to Twitter, Mohammad worked on WinRT Core in the operating systems group at Microsoft. Mohammad studied computer engineering at the University of Illinois at Urbana-Champaign where he specialized in real-time embedded systems.

" }, - + { - + "serial": 108272, "name": "Mike Amundsen", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_108272.jpg", @@ -8824,9 +8824,9 @@ "twitter": "mamund", "bio": "

An internationally known author and lecturer, Mike Amundsen travels throughout the world consulting and speaking on a wide range of topics including distributed network architecture, Web application development, and other subjects.

\n

In his role of API architect at Layer 7, Amundsen heads up the API Architecture and Design Practice in North America. He is responsible for working with companies to provide insight on how best to capitalize on the myriad opportunities APIs present to both consumers and the enterprise.

\n

Amundsen has authored numerous books and papers on programming over the last 15 years. His most recent book is a collaboration with Leonard Richardson titled “RESTful Web APIs”. His 2011 book, \u201cBuilding Hypermedia APIs with HTML5 and Node\u201d, is an oft-cited reference on building adaptable distributed systems.

" }, - + { - + "serial": 122599, "name": "Ishan Anand", "photo": null, @@ -8836,9 +8836,9 @@ "twitter": null, "bio": "

Ishan Anand is Director of New Products at Moovweb. He has been building and launching mobile products for the iPhone since the day it was released, and his work has been featured on TechCrunch, ReadWriteWeb and LifeHacker. Ishan has a solid background in software engineering with a focus on web and native application development for iOS devices and WebKit browsers. Prior to Moovweb, Ishan worked at Digidesign and his expertise was in multi-threaded real-time systems programming for the computer music industry. He holds dual-degrees in electrical engineering and mathematics from MIT.

" }, - + { - + "serial": 173201, "name": "John Anderson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173201.jpg", @@ -8848,9 +8848,9 @@ "twitter": "genehack", "bio": "

Lapsed biologist turned sysadmin turned programmer turned tech lead. Film at 11.

" }, - + { - + "serial": 179599, "name": "Edwin Aoki", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_179599.jpg", @@ -8860,9 +8860,9 @@ "twitter": null, "bio": "" }, - + { - + "serial": 143135, "name": "Yazz Atlas", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_143135.jpg", @@ -8872,9 +8872,9 @@ "twitter": "EntropyWorks", "bio": "

Yazz Atlas, who has been involved with open source projects for the past eighteen years, is currently working for the Advanced Technology Group within HP. His primary focus is replacing him self with a script and deploying OpenStack for the HP Cloud PaaS Core.

" }, - + { - + "serial": 4429, "name": "R Geoffrey Avery", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_4429.jpg", @@ -8884,9 +8884,9 @@ "twitter": "rGeoffrey", "bio": "

For many years a Perl Bioinformatics programmer in Philadelphia for a major pharma company helping scientists load, analyze and view their data.

" }, - + { - + "serial": 173455, "name": "Vishwas Babu", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173455.jpg", @@ -8896,9 +8896,9 @@ "twitter": "", "bio": "

Vishwas Babu is the lead engineer at the Mifos Initiative where he works on building an open technology platform for financial inclusion to the poor. He is also a co-founder of Conflux Technologies Private Limited, an organization based out of Bangalore, India which provides a multitude of financial solutions targeted toward microfinance institutions.

" }, - + { - + "serial": 108813, "name": "Jono Bacon", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_108813.jpg", @@ -8908,9 +8908,9 @@ "twitter": "jonobacon", "bio": "

Jono Bacon is a leading community manager, speaker, and author. Currently he works as Senior Director of Community at the XPRIZE Foundation and was formally the Ubuntu Community Manager at Canonical, optimizing and growing the global Ubuntu community.

\n

Bacon is a prominent author and speaker on community management and best practice, and wrote the best-selling The Art of Community (O\u2019Reilly), is the founder of the primary annual conference for community managers and leaders, the Community Leadership Summit, founder of the Community Leadership Forum, and is a regular keynote speaker at events about community management, leadership, and best practice.

\n

Bacon has provided community management consultancy for both internal and external communities for a range of organizations. This includes Deutsche Bank, Intel, SAP, Sony Mobile, Samsung, Open Compute Project, IBM, Dyson, Mozilla, National Finishing Contractors Association, AlienVault, and others.

\n

In addition to The Art of Community, Bacon co-authored Linux Desktop Hacks (O\u2019Reilly), Official Ubuntu Book (Prentice Hall), and Practical PHP and MySQL (Prentice Hall), and has written over 500 articles across 12 different publications. He writes regularly for a range of magazines.

\n

Bacon was the co-founder of the popular LugRadio podcast, which ran for four years with 2million+ downloads and 15,000 listeners, as well as spawning five live events in both the UK and the USA, and co-founded the Shot Of Jaq podcast. He co-founded the Bad Voltage podcast, a popular show about technology, Open Source, politics, and more. Bacon is also the founder of the Ubuntu Accomplishments, Jokosher, Acire, Python Snippets, and Lernid software projects.

\n

He lives in the San Francisco Bay Area in California with his wife, Erica, and their son, Jack.

" }, - + { - + "serial": 59574, "name": "Josh Barratt", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_59574.jpg", @@ -8920,9 +8920,9 @@ "twitter": "jbarratt", "bio": "

Josh joined leading (mt) Media Temple, a leading LA-based web hosting and cloud services company, as a system engineer in 2003 before making his way up to CTO and then Chief Architect. Josh’s world is a blend of computer science, software development, systems administration and the people and processes that tie it all together. Prior to Media Temple, he worked six years as a software engineer and has built everything from large clustered systems to embedded real-time motion control for special effects, and almost everything in between. Though experienced in a number of programming languages and environments, his current happy place is with Python.

" }, - + { - + "serial": 23017, "name": "Rick Barraza", "photo": null, @@ -8932,9 +8932,9 @@ "twitter": "rickbarraza", "bio": "

Rick Barraza has been working at the forefront of integrating design and technology for over a decade. A strong voice in the interactive design community, Rick is often engaged in promoting the balance of great experiences, creative technology and business motivators. Rick’s wide experience across many aspects of both design and development have made him a key asset at Cynergy where he works with a diverse clientele across a broad spectrum of needs- from branding and design to prototype development and experience architecture.

" }, - + { - + "serial": 180437, "name": "Doran Barton", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_180437.jpg", @@ -8944,9 +8944,9 @@ "twitter": "fozzmoo", "bio": "

Doran is a long-time open source advocate. A Unix user and instructor beginning in the early 1990s, he began using Linux in late 1994. In 1998, he started a consulting company in northern Utah promoting the use of Linux and open source solutions for businesses. His career has been split between systems administration/architecture and development.

\n

Doran now works as a senior developer at Bluehost, an Endurance International web hosting company. Leveraging his mixed background, he has helped move the company forward in using Software Collections to use more modern versions of Perl and other software on Enterprise Linux distributions.

" }, - + { - + "serial": 179963, "name": "David Baumgold", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_179963.jpg", @@ -8956,9 +8956,9 @@ "twitter": null, "bio": "

David Baumgold is a web developer and open source advocate based in the Boston area. He enjoys teaching, learning, and connecting interesting people with each other. He genuinely believes that everything will all work out in the end, somehow.

" }, - + { - + "serial": 152215, "name": "Brent Beer", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_152215.jpg", @@ -8968,9 +8968,9 @@ "twitter": "brntbeer", "bio": "

Brent Beer has used Git and GitHub for over 5 years through university classes, contributions to open source projects, and professionally as a web developer. He now enjoys his role teaching the world to use Git and GitHub to their full potential as a member of the GitHub Training team.

" }, - + { - + "serial": 7969, "name": "Brian Behlendorf", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_7969.jpg", @@ -8980,9 +8980,9 @@ "twitter": "brianbehlendorf", "bio": "

Brian Behlendorf is Managing Director at Mithril Capital Management in San Francisco. His career has been a mix of technology start-up, public policy, and non-profit tech leadership. Brian serves on the Boards of the Mozilla Foundation, the Electronic Frontier Foundation, and Benetech, three organizations using technology to fight for civil liberties, open technologies, and social impact in the digital domain. Prior to Mithril, Brian was Chief Technology Officer at the World Economic Forum. He also served for two years at the White House as advisor to the Open Government project within the Office of Science and Technology Policy, and then later as advisor to Health and Human Services on open software approaches to health information sharing. Before that he has founded two tech companies (CollabNet and Organic) and several Open Source software projects (Apache, Subversion, and more).

" }, - + { - + "serial": 170052, "name": "Tim Bell", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_170052.jpg", @@ -8992,9 +8992,9 @@ "twitter": "noggin143", "bio": "Tim Bell is responsible for the CERN IT Infrastructure Group which supports Windows, Mac and Linux across the site along with virtualisation, E-mail and web services. These systems are used by over 11,000 scientists researching fundamental physics, finding out what the Universe is made of and how it works. Prior to working at CERN, Tim worked for Deutsche Bank managing private banking infrastructure in Europe and for IBM as a Unix kernel developer and deploying large scale technical computing solutions." }, - + { - + "serial": 173340, "name": "Adam Benayoun", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173340.jpg", @@ -9004,9 +9004,9 @@ "twitter": "adambn", "bio": "

Adam is the co-founder and CEO of Binpress, a marketplace for commercial open source – providing a platform for building a profitable business from creating and working on open source projects.

\n

Adam has launched over 15 web ventures for the past 10 years and utilized numerous open source projects in the process of building his products.

\n

He studied Animation at the Minshar School of Arts and is an alumni of the 500startups accelerator in Mountain View.

" }, - + { - + "serial": 173233, "name": "Frederic Berg", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173233.jpg", @@ -9016,9 +9016,9 @@ "twitter": "frdrcbrg", "bio": "

Frederic is a product owner with 15 years of experience in the software industry living in Heidelberg, Germany. He is passionate about software development and a major driver behind the open sourcing of OpenUI5.

" }, - + { - + "serial": 137697, "name": "Tim Berglund", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_137697.jpg", @@ -9028,9 +9028,9 @@ "twitter": "tlberglund", "bio": "

Tim is a teacher, author, and technology leader with DataStax. He is a conference speaker internationally and in the United States, and contributes to the Denver, Colorado tech community as president of the Denver Open Source User Group. He is the co-presenter of various O\u2019Reilly training videos on topics ranging from Git to Mac OS X Productivity Tips to Apache Cassandra, and is the author of Gradle Beyond the Basics. He blogs very occasionally at timberglund.com, and lives in Littleton, CO, USA with the wife of his youth and their three children.

" }, - + { - + "serial": 106355, "name": "Andrew Berkowitz", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_106355.jpg", @@ -9040,9 +9040,9 @@ "twitter": "andrewberkowitz", "bio": "

Andrew Berkowitz is a founder and Chief Product Officer at TeamSnap.com. He is also Head Coach of ComedySportz Portland, a branch of the international Comedy Improv Theater.

" }, - + { - + "serial": 3397, "name": "Josh Berkus", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_3397.jpg", @@ -9052,9 +9052,9 @@ "twitter": "FuzzyChef", "bio": "

Josh Berkus is primarily known as one of the Core Team of the world-spanning open source database project PostgreSQL. As CEO of PostgreSQL Experts, Inc., he speaks on database and open source topics all over the world, and consults on database design, performance, and open source community building. He also makes pottery and is a darned good cook.

" }, - + { - + "serial": 10, "name": "Gina Blaber", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_10.jpg", @@ -9064,9 +9064,9 @@ "twitter": "ginablaber", "bio": "

VP of Conferences, O’Reilly. Interested in big data, web performance and operations, open source, publishing, location-based technologies, JavaScript, social media, and related topics. Love to hear about cutting edge content and speakers in these areas, and practical ideas for reaching a more diverse audience and speaker base.

" }, - + { - + "serial": 179435, "name": "Josh Bleecher Snyder", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_179435.jpg", @@ -9076,9 +9076,9 @@ "twitter": "josharian", "bio": "

Josh Bleecher Snyder is the Director Of Software Engineering at PayPal. He was a co-founder / CTO of Card.io, which was acquired by PayPal in 2012. Josh was leading card.io\u2019s technology development. Before card.io, Josh founded Treeline Labs, an iOS software development company, and was a Senior Software Engineer at AdMob where he was the first iOS engineer. Josh dropped out of the Philosophy Ph.D. program at Stanford University (ABD).

" }, - + { - + "serial": 2593, "name": "Michael Bleigh", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_2593.jpg", @@ -9088,9 +9088,9 @@ "twitter": "mbleigh", "bio": "

Michael is a JavaScript and Ruby developer and the CEO/Cofounder of Divshot, a static web hosting platform for developers. He has been working to make web development better through open source contributions (such as OmniAuth, Grape, and Themestrap) as well as by speaking at events including as RailsConf, Confoo, Open Source Bridge, and OSCON.

" }, - + { - + "serial": 143232, "name": "Olivier Bloch", "photo": null, @@ -9100,9 +9100,9 @@ "twitter": "obloch", "bio": "

I am Senior Technical Evangelist at Microsoft Open Technologies, Inc., looking into interoperability, cross platform and open source development for Microsoft client platforms: Windows Phone, Windows and IE.
\nPrior to this role I have been in the embedded space for almost 10 years prior to this, developing, consulting, training and talking about the Internet Of Things, connected devices and other cool and interesting topics in France, in the US and in other places around the world.
\nI am not just a geek who loves to play around with the latest gadgets\u2026 well, mostly a geek\u2026 but I love the idea that geeking around with these devices is all about what the consumer and industrial ecosystems are becoming these days.

" }, - + { - + "serial": 172898, "name": "Adam Bordelon", "photo": null, @@ -9112,9 +9112,9 @@ "twitter": "", "bio": "

Adam is a distributed systems engineer at Mesosphere and works on Apache Mesos.
\nBefore joining Mesosphere, Adam was lead developer on the Hadoop core team at MapR Technologies, he developed distributed systems for personalized recommendations at Amazon, and he rearchitected the LabVIEW compiler at National Instruments. He completed his Master\u2019s degree at Rice University, building a tool to analyze supercomputer performance data for bottlenecks and anomalies.

" }, - + { - + "serial": 180268, "name": "Jay Borenstein", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_180268.jpg", @@ -9124,9 +9124,9 @@ "twitter": "", "bio": "

I was born in Southern California. I did my undergraduate and graduate studies at Stanford, specializing in quantitative economics and operations research. I discovered both academic disciplines benefited greatly from computer simulation and I found my passions increasingly directed toward realizing products and experiences through software.

\n

Another thread in my personal and professional life has been a love for teams of people. I find few things to be as stimulating as a smart and motivated group of people coming together in a common pursuit.

\n

Combining the above interests, the culture of Silicon Valley (and Stanford itself, for that matter) has been a perfect fit for
\n me. After initially working as software engineer for a number of years and then plying the waters in technology management as a CTO, I founded Integration Appliance in 2000. I stepped down as CEO and away from day-to-day operations at IntApp in May, 2007 and served on the board through 2012. Today, IntApp is a successful and growing company.

\n

Part of the reason for stepping away from a mature business is the quest for new challenges. Another part is the desire to get
\n back to the root of where many great technologies and ideas emerge; academia. I currently teach computer science at Stanford University and also run Facebook Open Academy as part of a larger Facebook effort to modernize education. I find it very fulfilling to help motivated, bright minds grow and succeed.

" }, - + { - + "serial": 96208, "name": "Joe Bowser", "photo": null, @@ -9136,9 +9136,9 @@ "twitter": "infil00p", "bio": "

Joe is the creator of PhoneGap for Android and is the longest contributing committer to the PhoneGap and Apache Cordova projects respectively. When he is not contributing to Open Source at Adobe, he spends his spare time working on various hardware projects at home, as well as at the Vancouver Hack Space, which he co-founded.

" }, - + { - + "serial": 173248, "name": "Garth Braithwaite", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173248.jpg", @@ -9148,9 +9148,9 @@ "twitter": "garthdb", "bio": "

Previous lead designer on Brackets. Current developer on Topcoat. Core contributor for Open Source Design.

\n

Four daughters under the age of 9 (ladies man). Fanboy.

" }, - + { - + "serial": 159586, "name": "Alex Brandt ", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_159586.jpg", @@ -9160,9 +9160,9 @@ "twitter": "", "bio": "

Alex Brandt is a cloud developer at Rackspace, helping people build distributed scalable systems with a variety of technologies. An emerging evangelist for all things cloud, he has worked in IT support and solutioning as well as research in the realm of physics. Alex holds a B.S. in computer science and physics from Minnesota State University Moorhead.

" }, - + { - + "serial": 131404, "name": "VM Brasseur", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_131404.jpg", @@ -9172,9 +9172,9 @@ "twitter": "vmbrasseur", "bio": "

VM is a manager of technical people, projects, processes, products and p^Hbusinesses. In her over 15 years in the tech industry she has been an analyst, programmer, product manager, software engineering manager and director of software engineering. Currently she is splitting her time between shoeless consulting, a tech recruiting and management consulting firm, and writing a book translating business concepts into geek speak.

\n

VM blogs at “{a=>h}”:http://anonymoushash.vmbrasseur.com and tweets at @vmbrasseur.

" }, - + { - + "serial": 24978, "name": "Tim Bray", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_24978.jpg", @@ -9184,9 +9184,9 @@ "twitter": "timbray", "bio": "

Tim has co-founded two companies, helped raise five rounds of venture capital, written over a million words on his blog, edited the official specifications of both XML and JSON, worked for Sun Microsystems and Google, and is worried about passwords, Web culture, and tractable concurrency.

" }, - + { - + "serial": 25862, "name": "Michael Brewer", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_25862.jpg", @@ -9196,9 +9196,9 @@ "twitter": "operatic", "bio": "

Michael Brewer is an Application Programmer Specialist for the Franklin College Office of Information Technology at The University of Georgia. He designs database-backed web applications used by thousands of students and faculty and serves on several college and University-wide committees on Web development, best practices, and application security. In 2005, he won an Advising Technology Innovation Award from the National Academic Advising Association for an academic advising application he created and maintains. A speaker at OSCON in 2011 and 2012, he is also on the board of the United States PostgreSQL Association. He holds dual degrees in Mathematics and Music from The University of Georgia. A member of ASCAP, he also conducts the oldest continually operating community band in the state of Georgia; he has arranged music for orchestra, band, chorus, and has even composed incidental music for plays and musicals.

" }, - + { - + "serial": 6845, "name": "Joe Brockmeier", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_6845.jpg", @@ -9208,9 +9208,9 @@ "twitter": "jzb", "bio": "

Joe Brockmeier is a member of Red Hat’s Open Source and Standards
\n(OSAS) team, and is involved with Project Atomic, the Fedora Project’s
\nCloud Working Group, and is a member of the Apache Software Foundation.
\nBrockmeier has a long history of involvement with Linux and open source,
\nand has also spent many years working as a technology journalist.
\nBrockmeier has written for ReadWriteWeb, LWN, Linux.com, Linux Magazine,
\nLinux Pro Magazine, ZDNet, and many others.

" }, - + { - + "serial": 161486, "name": "Ethan Brown", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_161486.jpg", @@ -9220,9 +9220,9 @@ "twitter": "EthanRBrown", "bio": "

I work for Pop Art, an interactive marketing agency, doing mostly back-end website work. A lot of my current work is in C#/.NET, but I am shifting a lot of my attention to the Node.js stack (check out my projects on NPM!). My undergraduate work was in mathematics and computer science, and I have a broad and diverse background in software technologies.

" }, - + { - + "serial": 90628, "name": "Avi Bryant", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_90628.jpg", @@ -9232,9 +9232,9 @@ "twitter": "avibryant", "bio": "

Avi has led product, engineering, and data science teams at Etsy, Twitter and Dabble DB (which he co-founded and Twitter acquired). He\u2019s known for his open source work on projects such as Seaside, Scalding, and Algebird. Avi currently works at Stripe.

" }, - + { - + "serial": 148534, "name": "Brian Bulkowski", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_148534.jpg", @@ -9244,9 +9244,9 @@ "twitter": "aerospikedb", "bio": "

Brian Bulkowski, founder and CTO of Aerospike Inc. (formerly Citrusleaf), has 20-plus years experience designing, developing and tuning networking systems and high-performance Webscale infrastructures. He founded Aerospike after learning first hand, the scaling limitations of sharded MySQL systems at Aggregate Knowledge. As director of performance at this media intelligence SaaS company, Brian led the team in building and operating a clustered recommendation engine. Prior to Aggregate Knowledge, Brian was a founding member of the digital TV team at Navio Communications and chief architect of Cable Solutions at Liberate Technologies where he built the high-performance embedded networking stack and the Internetscale broadcast server infrastructure. Before Liberate, Brian was a lead engineer at Novell, where he was responsible for the AppleTalk stack for Netware 3 and 4.

" }, - + { - + "serial": 171495, "name": "Greg Bulmash", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171495.jpg", @@ -9256,9 +9256,9 @@ "twitter": "GregBulmash", "bio": "

Greg Bulmash is an urban legend, former senior editor at IMDb, and he makes really awesome chocolate cookies.

\n

During the days, he writes developer documentation for Microsoft, but evenings and weekends belong to Seattle CoderDojo where he somehow brings together dozens and dozens of kids, parents, and volunteers to run a free Saturday morning coding club.

" }, - + { - + "serial": 169673, "name": "Cody Bunch", "photo": null, @@ -9268,9 +9268,9 @@ "twitter": "cody_bunch", "bio": "

Cody Bunch is a Private Cloud / Virtualization Architect, VMware vExpert, and VMware VCP from San Antonio, TX. Cody has authored or co-authored several OpenStack and VMware books. Additionally he has been a tech editor on a number of projects. Cody also regularly speaks at industry events and local user groups.

" }, - + { - + "serial": 173431, "name": "Eric Butler", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173431.jpg", @@ -9280,9 +9280,9 @@ "twitter": "", "bio": "

Eric leads the Technology team at Webtrends, chartered with creating creative disruptive solutions in the analytics and optimization space. His team is responsible for the newly introduced Webtrends Streams product, among other patent-pending technologies recently released by Webtrends.

\n

Eric has held technology leadership positions at Webtrends, Jive Software and Intel in his career. He is a runner, follows his kids around playing soccer, and lives in Portland with his family.

" }, - + { - + "serial": 108884, "name": "Paris Buttfield-Addison", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_108884.jpg", @@ -9292,9 +9292,9 @@ "twitter": "parisba", "bio": "

Paris is co-founder of Secret Lab Pty. Ltd., leading production and design efforts in the mobile game and app development space. A frequent speaker at conferences, workshops and training sessions, Paris enjoys discussing engineering, product development, design and other facets of the mobile and game development worlds. Recent conferences include Apple Australia\u2019s /dev/world/2013 in Melbourne (and 2008, 2009, 2010, 2011, 2012), a keynote at CreateWorld Brisbane 2010 (and a speaker in 2009, 2011, 2012, and 2014), IxDA\u2019s Interaction 11 in Boulder (March 2011), XMediaLab Location-Based Services in Malmo, Sweden (January 2011), a tutorial and a session at OSCON 2011, OSCON 2012, OSCON 2013, linux.conf.au 2011, and many others.

\n

Paris is currently writing “Mobile Game Development With Unity” and “iOS Game Development Cookbook”, both for O’Reilly, and is the co-author of the books “Learning Cocoa with Objective-C Third Edition” (O’Reilly, 2012 and 2014), “iPhone and iPad Game Development For Dummies” (Wiley, 2010) and “Unity Mobile Game Development For Dummies” (Wiley, 2011).

\n

Paris is a highly experienced software developer, product and project manager. Key technologies include Objective-C/Cocoa on the Macintosh and iPhone/iPod Touch and iPad platforms, Java on Blackberry and Google Android and C# on Windows Mobile. Open GL ES and Unity are also favourites.

\n

Paris spent several years leading Meebo Inc.\u2019s mobile strategy in Mountain View, CA; Meebo was one of the world\u2019s fastest growing consumer internet companies and was acquired by Google in 2012. Paris is currently working on his next book, also with O’Reilly, and has recently submitted a PhD in Human-Computer Interaction, focusing on the use of tablets for information management. He is currently co-founder, producer, and occasional programmer at Secret Lab, in Tasmania, Australia.

" }, - + { - + "serial": 155881, "name": "Alejandro Cabrera", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_155881.jpg", @@ -9304,9 +9304,9 @@ "twitter": "cppcabrera", "bio": "

Hey! Nice to meet you. I’m a developer at Rackspace, and I work on the Openstack Marconi project during the day. I enjoy learning from others and sharing knowledge.

\n

Great friends, Linux, open source, Python, and Haskell have helped me get to where I am today.

" }, - + { - + "serial": 63576, "name": "Bryan Call", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_63576.jpg", @@ -9316,9 +9316,9 @@ "twitter": "", "bio": "

Bryan Call has been writing code and working with on large scale solutions for 14 years. He has experience optimizing and profiling projects, including Apache Traffic Server and many internal projects at Yahoo!.

\n

He came to Yahoo! through an acquisition of a startup and has been working there for the last 12 years. He has worked on various products and teams, such as WebRing, GeoCities, People Search, Yahoo! Personal, Tiger Team (internal consulting team), Architect in the Platform Group, Architect in the Edge Platform, and now is working in the R&D Systems Group.

\n

Bryan is also a commiter on the Apache Traffic Server project and instrumental in bring Traffic Server to the Apache Foundation.

" }, - + { - + "serial": 182198, "name": "Darryn Campbell", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_182198.jpg", @@ -9328,9 +9328,9 @@ "twitter": "", "bio": "

Darryn Campbell is the development lead on RhoMobile suite, the cross platform development framework provided by Motorola Solutions. He has been developing application development frameworks targeted at enterprise since 2008 seeing the product space increase from a small number of niche suppliers to the crowded marketplace we see today.

" }, - + { - + "serial": 155088, "name": "Francesc Campoy Flores", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_155088.jpg", @@ -9340,9 +9340,9 @@ "twitter": "francesc", "bio": "

Francesc Campoy Flores joined the Go team in 2012 as Developer Programs Engineer. Since then, he has written some considerable didactic resources and traveled the world attending conferences and organizing live courses.

\n

He joined Google in 2011 as a backend software engineer working mostly in C++ and Python, but it was with Go that he rediscovered how fun programming can be. He loves languages; fluent in four of them, he’s now tackling a fifth one.

" }, - + { - + "serial": 6921, "name": "Brian Capouch", "photo": null, @@ -9352,9 +9352,9 @@ "twitter": "", "bio": "

Brian Capouch is a longtime open source user, programmer, and hacker.

\n

He teaches CS using 100% Open Source tools at small Indiana college, run a small wireless ISP; Asterisk and openWRT are his specialities.

" }, - + { - + "serial": 109289, "name": "Thierry Carrez", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_109289.jpg", @@ -9364,9 +9364,9 @@ "twitter": "tcarrez", "bio": "

Thierry Carrez has been the Release Manager for the OpenStack project since its inception, coordinating the effort and facilitating collaboration between contributors. He is the elected chair of the OpenStack Technical Committee, which is in charge of the technical direction of the project. He spoke about OpenStack, open innovation and open source project management at various conferences around the world, including OSCON, LinuxCon and FOSDEM. A Python Software Foundation fellow, he was previously the Technical lead for Ubuntu Server at Canonical, and an operational manager for the Gentoo Linux Security Team.

" }, - + { - + "serial": 75349, "name": "Piers Cawley", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_75349.jpg", @@ -9376,9 +9376,9 @@ "twitter": "pdcawley", "bio": "

Piers Cawley started programming Perl in the mid nineties, but recently spent a few years working as a Ruby programmer.

\n

He’s currently writing Perl for Thermeon Europe

\n

He’s a singer and balloon modeller, and has created custom balloon millinery for Sarah Novotny.

" }, - + { - + "serial": 123894, "name": "Bill Cernansky", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_123894.jpg", @@ -9388,9 +9388,9 @@ "twitter": "", "bio": "

Bill Cernansky is a veteran Software Configuration Management Engineer at Jeppesen. Bill also performs and teaches improvisation and oversees tech at ComedySportz Portland. In addition, he is arguably the best at jumping over this one thing in his backyard.

" }, - + { - + "serial": 10595, "name": "Francesco Cesarini", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_10595.jpg", @@ -9400,9 +9400,9 @@ "twitter": "FrancescoC", "bio": "

Francesco Cesarini is the founder and technical director of Erlang Solutions Ltd. He has used Erlang on a daily basis since 1995, starting as an intern at Ericsson\u2019s computer science laboratory, the birthplace of Erlang. He moved on to Ericsson\u2019s Erlang training and consulting arm working on the first release of the OTP middleware, applying it to turnkey solutions and flagship telecom applications. In 1999, soon after Erlang was released as open source, he founded Erlang Solutions. With offices in five countries, they have become the world leaders in Erlang based support, consulting, training, certification, systems development and conferences. Francesco has worked in major Erlang based projects both within and outside Ericsson, and as Technical Director, has led the development and consulting teams at Erlang Solutions. He is also the co-author of Erlang Programming, a book published by O\u2019Reilly and is currently co-authoring Designing For Scalability With Erlang/OTP. He lectures the graduate students at Oxford University. You can follow his ramblings on twitter.

" }, - + { - + "serial": 837, "name": "Scott Chacon", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_837.jpg", @@ -9412,9 +9412,9 @@ "twitter": "", "bio": "

Scott Chacon is a cofounder and the CIO of GitHub. He is also the author of the Pro Git book by Apress and the maintainer of the Git homepage (git-scm.com). Scott has presented at dozens of conferences around the world on Git, GitHub and the future of work.

" }, - + { - + "serial": 147, "name": "Colin Charles", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_147.jpg", @@ -9424,9 +9424,9 @@ "twitter": "bytebot", "bio": "

Colin Charles works on MariaDB at SkySQL. He has been the Chief Evangelist for MariaDB since 2009, with work ranging from speaking engagements to consultancy and engineering works around MariaDB. He lives in Kuala Lumpur, Malaysia and had worked at MySQL since 2005, and been a MySQL user since 2000. Before joining MySQL, he worked actively on the Fedora and OpenOffice.org projects. He’s well known within open source communities in Asia and Australia, and has spoken at many conferences to boot.

" }, - + { - + "serial": 133198, "name": "Christopher Chedeau", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_133198.jpg", @@ -9436,9 +9436,9 @@ "twitter": "vjeux", "bio": "

Christopher “vjeux” Chedeau is a Front-End Engineer at Facebook. He is passionate about the web and its ability to very easily write great user interfaces.

" }, - + { - + "serial": 133360, "name": "Doris Chen", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_133360.jpg", @@ -9448,9 +9448,9 @@ "twitter": "doristchen", "bio": "

Dr. Doris Chen
\nhttp://blogs.msdn.com/b/dorischen/
\nTwitter @doristchen

\n

Doris is a Developer Evangelist at Microsoft for the Western region of the United States, specialized in web technologies (HTML5, jQuery, JavaScript, Ajax, and Java).
\nDoris has over 15 years of experience in the software industry working in several open source web tier technologies, Java platform, .NET and distributed computing technologies. She has developed and delivered over 400 keynotes, technical sessions, code camps worldwide, published widely at numerous international conferences and user groups including JavaOne, O\u2019Reilly, WebVisions, SD Forum, HTML5 and JavaScript meetups, and worldwide User Groups. Doris works very closely to create and foster the community around NetBeans, Glassfish, and related technologies. Before joining Microsoft, Doris Chen was a Technology Evangelist at Sun Microsystems.
\nDoris received her Ph.D. from the University of California at Los Angeles (UCLA) in computer engineering, specializing in medical informatics.

" }, - + { - + "serial": 171704, "name": "Roger Chen", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171704.jpg", @@ -9460,9 +9460,9 @@ "twitter": "rgrchen", "bio": "

Roger is an investor at O\u2019Reilly AlphaTech Ventures (OATV), where he looks for collisions between unmet needs and enabling technologies. He spent his past life as a scientist and engineer, alternating between academia and industry while dabbling in the startup and venture capital world. When he is not tinkering with technology, Roger plays sports and wonders what lies beyond bell curves. Roger has a BS from Boston University and a PhD from UC Berkeley, both in Electrical Engineering.

" }, - + { - + "serial": 133377, "name": "Sara Chipps", "photo": null, @@ -9472,9 +9472,9 @@ "twitter": "sarajchipps", "bio": "

Sara Chipps is a JavaScript developer and she blogs here. She is CTO of Flatiron School. In 2010 she started an organization called Girl Develop It which offers low cost software development classes geared towards women. Girl Develop It has had over 1000 unique students in New York City and now has 25 chapters around the world.

\n

She enjoys speaking to and meeting with diverse groups from the Girl Scouts to straight up code junkies. Her goal is to inspire more females to see that working with software is fun and glamorous.

" }, - + { - + "serial": 17417, "name": "Wendy Chisholm", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_17417.jpg", @@ -9484,9 +9484,9 @@ "twitter": "wendyabc", "bio": "

I am an author, activist and project manager. I co-wrote “Universal Design for Web Applications” with Matt May (O’Reilly, 2008) and edited Web Content Accessibility Guidelines 1.0 and 2.0–the basis of most web accessibility policies. I have appeared as Wonder Woman in a web comic with the other HTML5 Super Friends and as myself in interviews on Minnesota Public Radio, Puget Sound Public Radio, and at Ignite Seattle. In November 2009, I was the Seattle PI’s Geek of the Week.

\n

I\u2019ve focused on universal design since 1995. Being both a developer (B.S. in Computer Science) and a Human Factors Engineer (M.S. in Industrial Engineering/Human Factors), I bridge communication between developers and designers. As a Senior Strategist at Microsoft, I help make Bing services and apps accessible.

\n

Photo taken by Andy Farnum

" }, - + { - + "serial": 170054, "name": "Andrew Cholakian", "photo": null, @@ -9496,9 +9496,9 @@ "twitter": "andrewvc", "bio": "

Author of Exploring Elasticsearch, and developer/evangelist at Found.no, Andrew Cholakian is an active member of the Elasticsearch community. Additionally, he is behind several elasticsearch OSS projects as well, including the popular \u201cStretcher\u201d Elasticsearch library for the Ruby language. He\u2019s also a veteran of multiple startups in the LA area, and is an active member of the local startup community.

" }, - + { - + "serial": 172534, "name": "Robby Clements", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172534.jpg", @@ -9508,9 +9508,9 @@ "twitter": "robby_clements", "bio": "

Robby Clements is a software developer for Isotope11. His primary language has been Ruby for the past five years, but he’s been branching out into Erlang recently and finding it fascinating.

" }, - + { - + "serial": 6894, "name": "John Coggeshall", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_6894.jpg", @@ -9520,9 +9520,9 @@ "twitter": null, "bio": "

John Coggeshall is owner of Internet Technology Solutions, LLC. a high-end web consulting firm based in Michigan. He got started with PHP in 1997 and is the author of three published books and over 100 articles on PHP technologies with some of the biggest names in the industry such as Sams Publishing, Apress and O’Reilly. John also is a active contributor to the PHP core as the author of the tidy extension, a member of the Zend Education Advisory Board, and frequent speaker at PHP-related conferences worldwide.

" }, - + { - + "serial": 141235, "name": "C. Aaron Cois", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_141235.jpg", @@ -9532,9 +9532,9 @@ "twitter": "aaroncois", "bio": "

Aaron is a software engineer currently located in Pittsburgh, PA. He received his Ph.D. in 2007, developing algorithms and software for 3D medical image analysis. He currently leads a software development team at Carnegie Mellon University, focusing on web application development and cloud systems.

\n

Aaron is a polyglot programmer, with a keen interest in open source technologies. Some favorite technologies at the moment include Node.js, Python/Django, MongoDB, and Redis.

" }, - + { - + "serial": 99280, "name": "Tina Coleman", "photo": null, @@ -9544,9 +9544,9 @@ "twitter": "", "bio": "

Girl geek, web application development and architecture, agile advocate, community management

" }, - + { - + "serial": 4710, "name": "Damian Conway", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_4710.jpg", @@ -9556,9 +9556,9 @@ "twitter": "", "bio": "

Damian Conway is an internationally renowned speaker, author, and trainer, and a prominent contributor to the Perl community. Currently he runs Thoughtstream, an international IT training company that provides programmer training from beginner to masterclass level throughout Europe, North America, and Australasia. Most of his spare time over the past decade has been spent working with Larry Wall on the design and explication of the Perl 6 programming language. He has a PhD in Computer Science and was until recently an Adjunct Associate Professor in the Faculty of Information Technology at Monash University, Australia.

" }, - + { - + "serial": 179595, "name": "Danese Cooper", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_179595.jpg", @@ -9568,9 +9568,9 @@ "twitter": "DivaDanese", "bio": "

Danese Cooper has an 22-year history in the software industry and has long been an advocate for transparent development methodologies. Ms. Cooper joined PayPal in February 2014, and has held many leadership roles within the computer science sector. She has managed teams at Symantec and Apple Inc. and for six years served as Chief Open Source Evangelist for Sun Microsystems before leaving to serve as Senior Director for Open Source Strategies at Intel. She advised on open source policy to the R community while at REvolution Computing (now Revolution Analytics), and she served from February 2010 to July 2011 as Chief Technical Officer for the Wikimedia Foundation. She currently runs a successful consultancy to companies wishing to pursue open source strategies, which has served the SETI Foundation, Harris Corporation and the Bill & Melinda Gates Foundation among other clients. She is a director on the boards of the Drupal Association, and the Open Source Hardware Association, a board advisor for Mozilla and Ushahidi, and has served since 2005 as a Member of the Apache Software Foundation. She was a board member for 10 years at Open Source Initiative.

" }, - + { - + "serial": 172201, "name": "William Cox", "photo": null, @@ -9580,9 +9580,9 @@ "twitter": "gallamine", "bio": "

When William isn’t busy being a husband and father, he is an electrical engineer specializing in signal processing and machine learning. He’s worked on underwater robots, radar detection, algorithmic forex, and torpedo tracking. He tweets @gallamine and blogs at http://gallamine.com

" }, - + { - + "serial": 182463, "name": "Kevin Crocker", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_182463.jpg", @@ -9592,9 +9592,9 @@ "twitter": "", "bio": "

Kevin Crocker is a Consulting Instructor at Pivotal. He combines 40 years of technology awareness with 30 years of instructional expertise spanning multiple technologies and disciplines to bring a rich educational experience to his classes.

\n

His teaching background includes: Statistics; operations research; optimization theory; distance education; adult education; instructional design; UNIX and Linux operating systems, servers, and scripting; Java; business, banking, and finance; virtualization; and Data Analytics.

\n

He is a published author and award winning course designer. His guiding principle is: Driven by light bulbs!

\n

When he’s not helping people learn leading edge technology he’s busy with his family, golf, and home brewing special beers.

" }, - + { - + "serial": 169862, "name": "Adam Culp", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_169862.jpg", @@ -9604,9 +9604,9 @@ "twitter": "adamculp", "bio": "

Adam Culp, organizer of the SunshinePHP Developer Conference and South Florida PHP Users Group (SoFloPHP) where he speaks regularly, is a Zend Certified PHP 5.3 Engineer consulting for Zend Technologies. Adam is passionate about developing with PHP and enjoys helping others write good code, implement standards, refactor efficiently, and incorporate unit and functional tests into their projects. When he is not coding or contributing to various developer communities, he can be found hiking around the United States National Parks, teaching judo, or long distance running.

" }, - + { - + "serial": 183161, "name": "Jim Cupples", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_183161.jpg", @@ -9616,9 +9616,9 @@ "twitter": null, "bio": "

Jim is a political science nerd who has minimal tech chops, but a deep love of Open Source. Jim is currently working with college students from around Oregon (especially PSU) on a project called Ballot Path that will allow users to see all of their elected reps, and how they can replace them. A side project of Ballot Path is creating Open Source GIS political boundary maps (we have to create many of them from scratch for the smaller elected offices, especially in rural areas), which will hopefully be used by people looking to help shape a world that they want their kids to live in.

\n

“Fortune may have yet a better success in reserve for you, and they who lose today may win tomorrow.” Cervantes

" }, - + { - + "serial": 116276, "name": "Patrick Curran", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_116276.jpg", @@ -9628,9 +9628,9 @@ "twitter": "", "bio": "

Patrick Curran is Chair of the JCP. In this role he oversees the activities of the organization’s Program Management Office including driving the process, managing its membership, guiding specification leads and experts through the process, leading Executive Committee meetings, and managing the JCP.org web site.

\n

Patrick has worked in the software industry for more than 25 years, and at Sun (and now Oracle) for almost 20 years. He has a long-standing record in conformance testing, and most recently led the Java Conformance Engineering team in Sun’s Client Software Group. He was also chair of Sun’s Conformance Council, which was responsible for defining Sun’s policies and strategies around Java conformance and compatibility.

\n

Patrick has participated actively in several consortia and communities including the W3C (as a member of the Quality Assurance Working Group and co-chair of the Quality Assurance Interest Group), and OASIS (as co-chair of the Test Assertions Guidelines Technical Committee). Patrick’s blog is here.

" }, - + { - + "serial": 173396, "name": "Benjamin Curtis", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173396.jpg", @@ -9640,9 +9640,9 @@ "twitter": "stympy", "bio": "

Ben has been developing web apps and building startups since ’99, and fell in love with Ruby and Rails in 2005. Before co-founding Honeybadger, he launched a couple of his own startups: Catch the Best, to help companies manage the hiring process, and RailsKits, to help Rails developers get a jump start on their projects.

\n

Ben’s role at Honeybadger ranges from bare-metal to front-end… he keeps the server lights blinking happily, builds a lot of the back-end Rails code, and dips his toes into the front-end code from time to time.

\n

When he’s not working, Ben likes to hang out with his wife and kids, ride his road bike, and of course hack on open source projects.

" }, - + { - + "serial": 108736, "name": "Michael Dale", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_108736.jpg", @@ -9652,9 +9652,9 @@ "twitter": "michael_dale", "bio": "

Michael is the lead front end architect for Kaltura Open Source Video Platform. In 2006 Michael Dale co-founded metavid.org an open video community archive of US house and senate floor proceedings. Later he worked on video for Wikipedia. With Kaltura he has worked on HTML5 video for Wikipedia the Internet Archive and dozens of major web properties that make use of Kaltura’s platform. His work includes work on scalable video delivery across numerous browsers and devices, subtitling, advertising, analytics and video editing in HTML5.

" }, - + { - + "serial": 172988, "name": "Avik Das", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172988.jpg", @@ -9664,9 +9664,9 @@ "twitter": "", "bio": "

I graduated from UC Berkeley with Bachelor’s degrees in EECS and Math, and have since worked at LinkedIn as a software developer. I currently am the lead for the server that powers the LinkedIn iPad application.

\n

In my free time, I love working out, and when I find the time, I like to dabble in cooking.

" }, - + { - + "serial": 131890, "name": "Jennifer Davidson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_131890.jpg", @@ -9676,9 +9676,9 @@ "twitter": "jewifer", "bio": "

Jennifer Davidson is Program Manager for ChickTech, a nonprofit geared toward getting more girls and women interested and retained in technology careers. She is a PhD candidate in Computer Science with a minor in Aging Sciences at Oregon State University. She is currently working on research related to involving older adults in the design and development of open source software. Her research bridges the fields of human-computer interaction, open source software communities, and gerontechnology. She is also the Community Manager for Privly, an open source project related to internet privacy.

" }, - + { - + "serial": 172536, "name": "A. Jesse Jiryu Davis", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172536.jpg", @@ -9688,9 +9688,9 @@ "twitter": "jessejiryudavis", "bio": "

Senior Python Engineer at MongoDB in New York City. Author of Motor, an async MongoDB driver for Tornado, and of Toro, a library of locks and queues for Tornado coroutines. Contributor to Python, PyMongo, MongoDB, Tornado, and Tulip.

" }, - + { - + "serial": 173393, "name": "Kristen Dedeaux", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173393.jpg", @@ -9700,9 +9700,9 @@ "twitter": "", "bio": "

Kristen Dedeaux is a technical writer and an open source enthusiast. She has a passion for the written word and enjoys sharing her editorial expertise with others. Throughout her seven-year career as a content manager and editor in the fast-paced financial industry, she has championed several successful training programs and designed numerous writing workshops. Within the past year, she created an internal corporate blog, “The Writer\u2019s Block: Tips for Better Business Writing”, which received positive feedback from users across the globe, particularly from software engineers. The fastest red pen in the West, she can wrangle any stubborn sentence into shape.

" }, - + { - + "serial": 173414, "name": "Ethan Dereszynski", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173414.jpg", @@ -9712,9 +9712,9 @@ "twitter": "", "bio": "

Ethan Dereszynski is a research scientist in machine learning and data mining at Webtrends. He earned his PhD in computer science at Oregon State University in 2012. Ethan\u2019s research focuses on the application of machine learning, in particular Bayesian statistics and probabilistic models, to challenging problems across multiple disciplines. Combining work and play, Ethan is also interested in unsupervised approaches for learning models of player behavior in real-time strategy games (read: accepting all challengers at StarCraft). Prior to studying at Oregon State, he received a B.S. in computer science at Alma College, where he minored in Mathematics and English.

" }, - + { - + "serial": 174072, "name": "Henning Diedrich", "photo": null, @@ -9724,9 +9724,9 @@ "twitter": "hdiedrich", "bio": "

Henning is an entrepreneur, programmer and game designer. He is the Lead Software Engineer / Berlin at SumUp, a European mobile POS company. Henning is the creator of the Erlang VoltDB driver Erlvolt and a maintainer of the Erlang MySQL driver Emysql. His Open Source contributions for Erlang, Lua, MySQL and VoltDB are direct results of what pieces he found missing for a better game server stack.

\n

Henning wrote his first games on the C64, develops for the web since Netscape 1.0 and produced his first game server with Java 1.0. He created a language to describe tariffs for AXA and programmed and produced browser games. He founded Eonblast to create a more immersive online game experience and as official cover for the Time Tuner mission.

\n

Starting out on a 1MHz CPU, Henning’s special interest tends to be speed as an enabler. He has talked about evil performance hacks at the annual Lua Workshop, about his record setting Node.js VoltDB benchmark, and was elected to explain ‘Why Erlang?’ to the game developer community at the GDC Online 2012 (15.000 hits on slideshare.) He also contributed the underrated sed script markedoc to the OTP stack, which converts markdown to edoc.

" }, - + { - + "serial": 122516, "name": "Renee DiResta", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_122516.jpg", @@ -9736,9 +9736,9 @@ "twitter": "noupside", "bio": "

Renee DiResta is a Principal at O’Reilly AlphaTech Ventures (OATV), where she evaluates seed-stage investments. Prior to joining OATV in June of 2011, Renee spent seven as a trader at Jane Street Capital, a quantitative proprietary trading firm in New York City. She is interested in meeting interesting startups, data science, and improving liquidity and transparency in private markets.

" }, - + { - + "serial": 172973, "name": "Connor Doyle", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172973.jpg", @@ -9748,9 +9748,9 @@ "twitter": "nor0101", "bio": "

Connor Doyle is a software engineer at Mesosphere, focused on developing tools around Apache Mesos. Before joining Mesosphere, Connor worked at the design firm Gensler where he worked on process improvement, internal libraries and distributed architecture in Scala. He completed his Master’s degree at the University of Wisconsin – La Crosse, where his capstone project was a distributed simulator for doing multi-agent learning research.

" }, - + { - + "serial": 173105, "name": "Drasko Draskovic", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173105.jpg", @@ -9760,9 +9760,9 @@ "twitter": "draskodraskovic", "bio": "

Engineer – artist, Dra\u0161ko usualy builds computers with operating systems in less than 5 square millimeters. He holds MSc degree in Electrical Engineering and has over 10 years of expertise in embedded systems, semicoductor and telecommunications industry, where he hacked the things beyond the limits.

\n

Dra\u0161ko earned his reputation in Open Source community by being constantly involved and contributing to several projects dealing with low-level kernel programming and Linux device drivers like WeIO platform, OpenOCD JTAG debugger or CodeZero L4 hypervisor. He constantly walks on the thin line where hardware meets the software.

" }, - + { - + "serial": 137149, "name": "Clinton Dreisbach", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_137149.jpg", @@ -9772,9 +9772,9 @@ "twitter": "cndreisbach", "bio": "

Clojure and Python hacker for the Consumer Financial Protection Bureau. Lead developer on Qu, the CFPB’s public data platform, and contributor to Clojure, Hy, and other open source projects.

" }, - + { - + "serial": 123516, "name": "Emily Dunham", "photo": null, @@ -9784,9 +9784,9 @@ "twitter": "", "bio": "

Emily is a senior in computer science at Oregon State University. Since joining the OSU Open Source Lab in April 2011 a software developer on the Ganeti Web Manager project, she has worked as an intern at Intel, a teaching assistant in the computer science department, and a systems engineer at the OSL. She founded the OSL’s DevOps Bootcamp outreach program in August 2013, and is involved with the OSU Linux Users Group and local FIRST Robotics competitions.

" }, - + { - + "serial": 94695, "name": "David Elfi", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_94695.jpg", @@ -9796,9 +9796,9 @@ "twitter": "", "bio": "

Certainly, David has gained experience in all the different roles he played at Intel since 2008. Mainly based on Ecommerce products guided by AppUp product, he worked for different product flavors in the areas of web services, consumer experience and mobile application development.
\nRecently, he entered in the world of managing data for business analysis in the research of improvements based on data collected from the field.

" }, - + { - + "serial": 140062, "name": "Jonathan Ellis", "photo": null, @@ -9808,9 +9808,9 @@ "twitter": "spyced", "bio": "

Jonathan is CTO and co-founder at DataStax. Prior to DataStax, Jonathan worked extensively with Apache Cassandra while employed at Rackspace. Prior to Rackspace, Jonathan built a multi-petabyte, scalable storage system based on Reed-Solomon encoding for backup provider Mozy. In addition to his work with DataStax, Jonathan is project chair of Apache Cassandra.

" }, - + { - + "serial": 172370, "name": "Michael Enescu", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172370.jpg", @@ -9820,9 +9820,9 @@ "twitter": "michaelenescu", "bio": "

Michael Enescu is CTO of Open Source Initiatives at Cisco, leading open source programs across multiple technologies and product lines. Previously he served as the first Vice President of Product at XenSource (acquired by Citrix) and one of the first employees. He has a broad range of experience having developed and delivered over two dozen enterprise and consumer software products. Previously he was a founding member of the Mobile Web Services group at Palm and a founding member of the Java Content and J2ME teams at Sun. He led the development of first streaming video servers and digital libraries at SGI. He started his career in storage virtualization at IBM where he managed the development of the earlier versions of IBM\u2019s core middleware platform product in the WebSphere suite, along with leading development in expert systems, operating systems and virtualization in IBM’s Storage Products group. Michael has a BS degree from Caltech and MS in Computer Science from Stanford University.

" }, - + { - + "serial": 169992, "name": "Catherine Farman", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_169992.jpg", @@ -9832,9 +9832,9 @@ "twitter": "cfarm", "bio": "

Catherine Farman is a Developer at Happy Cog, where she builds standards-based websites using HTML, CSS and Javascript. Catherine has taught responsive web design, Javascript, and Sass courses for Girl Develop It. She\u2019s also helped develop new courses and has written open source curricula to share with other teachers. When she’s not at a computer she likes to sew and watch soccer.

" }, - + { - + "serial": 6631, "name": "Paul Fenwick", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_6631.jpg", @@ -9844,9 +9844,9 @@ "twitter": "pjf", "bio": "

Paul Fenwick is the managing director of Perl Training Australia, and has been teaching computer science for over a decade. He is an internationally acclaimed presenter at conferences and user-groups worldwide, where he is well-known for his humour and off-beat topics.

\n

In his spare time, Paul’s interests include security, mycology, cycling, coffee, scuba diving, and lexically scoped user pragmata.

\n

*Photograph by Joshua Button.

" }, - + { - + "serial": 173433, "name": "Mark Ferree", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173433.jpg", @@ -9856,9 +9856,9 @@ "twitter": "mrf", "bio": "

I am a developer who quickly grew to rely on open source software and tools for all of my projects.

\n

An active Drupal developer for the last seven years I am currently working as the Director of Engineering at Chapter Three where we use Drupal to power large content-managed sites.

" }, - + { - + "serial": 1639, "name": "Edward Finkler", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_1639.jpg", @@ -9868,9 +9868,9 @@ "twitter": "", "bio": "

Ed Finkler, also known as Funkatron, started making web sites before browsers had frames. He does front-end and server-side work in Python, PHP, and JavaScript.

\n

He served as web lead and security researcher at The Center for Education and Research in Information Assurance and Security (CERIAS) at Purdue University for 9 years. Now he’s a proud member of the Fictive Kin team. Along with Chris Hartjes, Ed is co-host of the Development Hell podcast.

\n

Ed’s current passion is raising mental health awareness in the tech community with his Open Sourcing Mental Illness speaking campaign. He is part of Engine Yard’s Prompt campaign.

\n

Ed writes at funkatron.com.

" }, - + { - + "serial": 152242, "name": "Keith Fiske", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_152242.jpg", @@ -9880,9 +9880,9 @@ "twitter": "keithf4", "bio": "

Database Administrator with OmniTI, Inc

" }, - + { - + "serial": 181484, "name": "Tyler Fitch", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_181484.jpg", @@ -9892,9 +9892,9 @@ "twitter": "", "bio": "

Tyler recently joined CHEF as a Customer Success Engineer – championing
\nbest practices and delightful experiences in automation. Prior to
\nworking at Chef he spent a decade as an engineer for Adobe developing
\nand automating commerce services for adobe.com using
\na variety of technologies. He lives in Vancouver, WA and when he’s not
\nprogramming enjoys lacrosse and using his passport.

" }, - + { - + "serial": 178139, "name": "Beth Flanagan", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_178139.jpg", @@ -9904,9 +9904,9 @@ "twitter": "dirtycitybird", "bio": "

Beth ‘pidge’ Flanagan is a Senior Software Engineer with Intel’s Opensource Technology Center. She spends most of her work life hacking on OpenEmbedded and the Yocto Project. She is the release engineer for the Yocto Project, maintainer of the yocto-autobuilder and pilot of the Yocto Blimp.

" }, - + { - + "serial": 46421, "name": "Richard Fontana", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_46421.jpg", @@ -9916,9 +9916,9 @@ "twitter": "", "bio": "

Richard Fontana is a lawyer at Red Hat, with particular responsibility
\nfor legal issues arising out of the software development process.
\nRichard specializes in copyright, trademark and patent issues,
\ntechnology transactions, free software/open source issues, computing
\ntechnology standards, data privacy and protection, information
\nsecurity, and legal matters relating to cloud computing. Richard is the
\nsole open source legal specialist at Red Hat, which is the world’s
\nlargest provider of open source-based enterprise software and cloud
\nsolutions, and he has been an active and influential public speaker on
\nmatters at the intersection of open source, law and policy. In
\naddition, Richard is an Individual Member-elected Board Director
\nof the Open Source Initiative.

" }, - + { - + "serial": 2650, "name": "Neal Ford", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_2650.jpg", @@ -9928,9 +9928,9 @@ "twitter": "neal4d", "bio": "

Neal Ford is Software Architect and Meme Wrangler at *Thought*Works, a global IT consultancy with an exclusive focus on end-to-end software development and delivery. He is also the designer and developer of applications, instructional materials, magazine articles, courseware, video/DVD presentations, and author and/or editor of 6 books spanning a variety of technologies, including the most recent The Productive Programmer. He focuses on designing and building of large-scale enterprise applications. He is also an internationally acclaimed speaker, speaking at over 100 developer conferences worldwide, delivering more than 600 talks. Check out his web site at www.nealford.com. He welcomes feedback and can be reached at nford@thoughtworks.com.

" }, - + { - + "serial": 142995, "name": "Steve Francia", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_142995.jpg", @@ -9940,9 +9940,9 @@ "twitter": "spf13", "bio": "

Steve Francia is the creator of hugo, cobra, nitro & spf13-vim. An author of multiple O’Reilly books, Steve blogs at spf13.com and gives many talks and workshops around the world. He is the Chief Developer Advocate at MongoDB responsible for the developer experience of MongoDB and leads the software engineering team responsible for drivers and integrations with all languages, libraries and frameworks. He loves open source and is thrilled to be able to work on it full time. When not coding he enjoys skateboarding and having fun outdoors with his wife and four children.

" }, - + { - + "serial": 28902, "name": "Roberto Galoppini", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_28902.jpg", @@ -9952,9 +9952,9 @@ "twitter": null, "bio": "

Roberto is a computer industry insider of 15+ years standing. Up until 1994 Roberto had never heard of Linux, until he chanced to lead a group of geeks in starting up a mobile ISP with just a bunch of old PCs. Since then Roberto has worked in such hands-on roles as programmer and systems analyst, eventually founding an open source firm open source firm in 2001, and an open source consortium in 2004.

\n

Roberto has taken an active interest in several free/open source software organizations. He currently serves on the Advisory Board of the SourceForge Marketplace and acts as the Institutional Relationship Manager for the OpenOffice.org Italian Association. Since 2003 Roberto has researched the economics of OSS, collaborating with universities and EC funded research projects. Roberto is a technical writer for IT and computer-related magazines, he regularly keeps a blog on Commercial Open Source at http://robertogaloppini.net.

" }, - + { - + "serial": 6852, "name": "Matthew Garrett", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_6852.jpg", @@ -9964,9 +9964,9 @@ "twitter": null, "bio": "

Matthew Garrett is a Linux kernel developer, firmware wrangler and meddler in cloud security. By day he works at Nebula, dealing with parts of the cloud that can be used to scare small children. By night, those parts of the cloud scare him.

" }, - + { - + "serial": 177325, "name": "Omri Gazitt", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_177325.jpg", @@ -9976,9 +9976,9 @@ "twitter": "", "bio": "

Omri Gazitt serves as VP of Engineering of HP\u2019s Helion Platform team, which delivers core parts of the Helion OpenStack and Helion Development Platform products, targeted at Enterprises that want to stand up their own Cloud on-premises, or consume it as a hosted platform in HP\u2019s Managed and Public Clouds. Omri\u2019s team is responsible for Identity & Access, Metering, Monitoring, Management Console, Load Balancing, DNS, Database, Messaging, Application Lifecycle, Developer Experience, and User Experience, and contributes heavily into the OpenStack and Cloud Foundry open source projects.

\n

In prior lives, Omri helped create .NET, Web Services, and Azure, as well as the Xbox and Kinect developer platforms. He lives with his wife and three daughters in Redmond, WA, and loves rainy winter days in the northwest because they mean fresh powder on the weekends.

" }, - + { - + "serial": 132564, "name": "Trisha Gee", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_132564.jpg", @@ -9988,9 +9988,9 @@ "twitter": "trisha_gee", "bio": "

Trisha is a developer at MongoDB. She has expertise in Java high performance systems, is passionate about enabling developer productivity, and has a wide breadth of industry experience from the thirteen years she’s been a professional developer. Trisha is a leader in the London Java Community and the Sevilla Java & MongoDB Communities, she believes we shouldn’t all have to make the same mistakes again and again.

" }, - + { - + "serial": 172751, "name": "Vidhya Gholkar", "photo": null, @@ -10000,9 +10000,9 @@ "twitter": "vgholkar", "bio": "

With a PhD in Signal Processing has developed new Machine Learning technologies for oil and gas exploration (Schlumberger). Managed Open Communication technology Standards at Vodafone Group. CTO at mobile startup (Argogroup) which has now been acquired.

" }, - + { - + "serial": 171201, "name": "Adam Gibson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171201.jpg", @@ -10012,9 +10012,9 @@ "twitter": "agibsonccc", "bio": "

Adam Gibson is a deep\u00ad learning specialist based in San Francisco assisting Fortune 500 companies, hedge funds, PR firms and startup accelerators with their machine learning projects. Adam has a strong track record helping companies handle and interpret big real\u00ad-time data. Adam has been a computer nerd since he was 13 and actively contributes to the open\u00ad source community.

" }, - + { - + "serial": 150440, "name": "Kelsey Gilmore-Innis", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_150440.jpg", @@ -10024,9 +10024,9 @@ "twitter": "kelseyinnis", "bio": "

Kelsey Gilmore-Innis is a back-end engineer at Reverb, where she uses Scala & functional programming to build powerful, scalable software systems. She strives to write code with charisma, uniqueness, nerve and talent and hopes to one day really, truly, deeply understand what a monad is.

" }, - + { - + "serial": 152123, "name": "Sebastien Goasguen", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_152123.jpg", @@ -10036,9 +10036,9 @@ "twitter": "sebgoa", "bio": "

Sebastien has over 15 years experience in the computing area, after a career in academia that led him to be an associate professor at Clemson University he joined Citrix to participate in the Apache CloudStack community. He is now an Apache committer and PMC member in CloudStack and Libcloud.

" }, - + { - + "serial": 173336, "name": "Sara Golemon", "photo": null, @@ -10048,9 +10048,9 @@ "twitter": "saramg", "bio": "

I work on PHP, HHVM, and wrote libssh2. I wrote a sizable chunk of Yahoo! Search’s front end, and now I make Facebook fast.

" }, - + { - + "serial": 173116, "name": "Jesus M. Gonzalez-Barahona", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173116.jpg", @@ -10060,9 +10060,9 @@ "twitter": "jgbarah", "bio": "

Jesus M. Gonzalez-Barahona is co-founder of Bitergia, the software development analytics company specialized in the analysis of free / open source software projects. He also teaches and researches in Universidad Rey Juan Carlos (Spain), in the context of the GSyC/LibreSoft research group. His interests include the study of communities of software development, with a focus on quantitative and empirical studies. He enjoys taking photos of the coffee he drinks around the world .

" }, - + { - + "serial": 156989, "name": "Patricia Gorla", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_156989.jpg", @@ -10072,9 +10072,9 @@ "twitter": "patriciagorla", "bio": "

Patricia Gorla is an Apache Cassandra Architect at The Last Pickle, a Cassandra consultancy.

\n

She has been involved in all aspects of software development, from server administration to application development, and from data analysis to data storage.

\n

She has worked with companies and governmental entities on all aspects of data migration to non-relational data stores, and training the technical teams on the new architecture.

\n

She helped the US Patent and Trademark Office ingest more than 6 million patent documents and images; architect secure search systems for a large mortgage insurer; and introduce Cassandra to a digital marketing firm’s data pipeline.

\n

Prior to architecting databases Patricia focused on the analysis and visualization of data.

\n

Patricia speaks often at conferences and meetups such as O’Reilly’s StrataConf + Hadoop World, the Datastax Cassandra Summits, and local user groups. She was also voted a DataStax MVP for Apache Cassandra by the community.

" }, - + { - + "serial": 180122, "name": "Jessica Goulding", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_180122.jpg", @@ -10084,9 +10084,9 @@ "twitter": null, "bio": "

Jessica spent 8 years in marketing, becoming an expert in SEO and Social Media, when a passion sparked for web development. Her interest began while doing marketing and customizing Wordpress themes to help small business clientele build websites. After two years, she decided to dive right into being a full-time developer and in the summer of 2012 I taught myself Ruby on Rails and advanced HTML and CSS. She has since worked with everything from the command line, git, bootstrap integration, heroku, amazon web services, pivotal tracker, postgres and mysql database setup.

\n

A forever student, after entering the world of development she gained a passion for attending developer conferences and leaving more inspired and knowledgable. Currently she is attending Turing.IO, a software development school based in Denver, Colorado.

" }, - + { - + "serial": 173325, "name": "Neel Goyal", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173325.jpg", @@ -10096,9 +10096,9 @@ "twitter": "", "bio": "

Neel is a R&D developer at Verisign with over ten years of experience. He
\nhas worked on a variety of initiatives for Verisign ranging from domain related tools, API specifications, and routing protocol implementations. He also serves as a member on Verisign\u00b9s Open Source Committee. Prior to Verisign, Neel worked at a startup developing consumer software for embedded devices and PCs.

" }, - + { - + "serial": 132323, "name": "Adam Granicz", "photo": null, @@ -10108,9 +10108,9 @@ "twitter": "granicz", "bio": "

Adam Granicz is a long-time F# insider and key community member, and the co-author of four F# books, including Expert F# 3.0 with Don Syme, the designer of the language. His company IntelliFactory specializes in consulting on the most demanding F# projects; shaping the future of the development of F# web, mobile and cloud applications; and developing WebSharper, the main web development framework for F#.

" }, - + { - + "serial": 183169, "name": "Brian Grant", "photo": null, @@ -10120,9 +10120,9 @@ "twitter": "", "bio": "

Brian Grant is an engineer at Google. He was formerly a lead of
\nGoogle\u2019s internal cluster management system, codenamed Borg, and was a founder of the Omega project. He now contributes to Google\u2019s cloud and open-source container efforts.

" }, - + { - + "serial": 179224, "name": "Chad Granum", "photo": null, @@ -10132,9 +10132,9 @@ "twitter": "", "bio": "

Current maintainer of Fennec (perl test tool, not the browser), Child.pm, and Test::Builder/Simple/More

" }, - + { - + "serial": 181009, "name": "James Grierson", "photo": null, @@ -10144,9 +10144,9 @@ "twitter": "jamesgrierson", "bio": "

James Grierson is the current Chief Operating Officer at Bluehost.com which powers over 2 million websites worldwide. During his time at Bluehost, James co-founded SimpleScripts automated installer for Open Source applications (merged with the MOJO Marketplace in 2013) and created the Bluehost Open Source Solutions program to support Open Source Communities. These programs provide Open Source projects with a distribution model, user feedback loop, discounted hosting services, financial support and leadership consulting.

" }, - + { - + "serial": 171381, "name": "John Griffith", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171381.jpg", @@ -10156,9 +10156,9 @@ "twitter": "solidfire", "bio": "

John Griffith is a Senior Software Engineer at SolidFire, where his primary responsibility is driving the OpenStack Cinder Block Storage Project. John is an open source expert serving as the project team lead for Cinder. He leads the team to provide first-class SolidFire product functionality and Quality of Service integration within OpenStack. John is also responsible for the development of a true Quality of Service storage appliance designed and built for the cloud.
\nHe has significant software engineering experience, most recently serving as lead software engineer at HP. During his tenure, John focused on building large scale fibre channel SANS to continuously test and develop storage and lead the development on a Unified Storage API.

\n

John holds a bachelor\u2019s degree from Regis University in Denver, Colorado.

" }, - + { - + "serial": 170237, "name": "Sarah Guido", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_170237.jpg", @@ -10168,9 +10168,9 @@ "twitter": "sarah_guido", "bio": "

Sarah is a data scientist at Reonomy, where she’s helping to build disruptive tech in the commercial real estate industry in New York City. Three of her favorite things are Python, data, and machine learning.

" }, - + { - + "serial": 2397, "name": "Andy Gup", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_2397.jpg", @@ -10180,9 +10180,9 @@ "twitter": "agup", "bio": "

Andy Gup is a Tech Lead at Esri where he focuses on web and mobile geo-spatial APIs. He is an active contributor to a number of open source projects in the geo community.

\n

His background includes working with a wide variety of cutting edge technologies from small websites and mobile apps to high-performance Fortune 500 enterprise systems.

" }, - + { - + "serial": 143377, "name": "Arun Gupta", "photo": null, @@ -10192,9 +10192,9 @@ "twitter": "arungupta", "bio": "

Arun Gupta is Director of Developer Advocacy at Red Hat and focuses on JBoss Middleware. As a founding member of the Java EE team at Sun Microsystems, he spread the love for technology all around the world. At Oracle, he led a cross-functional team to drive the global launch of the Java EE 7 platform through strategy, planning, and execution of content, marketing campaigns, and program. After authoring ~1400 blogs at blogs.oracle.com/arungupta on different Java technologies, he continues to promote Red Hat technologies and products at blog.arungupta.me. Arun has extensive speaking experience in 37 countries on myriad topics and is a JavaOne Rockstar. An author of a best-selling book, an avid runner, a globe trotter, a Java Champion, he is easily accessible at @arungupta.

" }, - + { - + "serial": 131884, "name": "Florian Haas", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_131884.jpg", @@ -10204,9 +10204,9 @@ "twitter": "", "bio": "

Florian is an open source software specialist, experienced technical consultant, seasoned training instructor, and frequent public speaker. He has spoken at conferences like LinuxCon, OSCON, linux.conf.au, the OpenStack Summit, the MySQL Conference and Expo, and countless user and meetup groups across the globe.

" }, - + { - + "serial": 171418, "name": "Steve Hannah", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171418.jpg", @@ -10216,9 +10216,9 @@ "twitter": "shannah78", "bio": "

I am a software developer because I love to create things and software imposes the fewest limitations on my creativity of any medium. Computer + Idea + Time + Perspiration = Any outcome you want.

\n

I started out with a G3 Bondi-Blue iMac and a free copy of Adobe Page Mill in 1998, but quickly expanded my horizon to include HTML, Javascript, PERL, Flash 3, and finally PHP 3 – in that order. Finding myself spending hours reading and writing web apps before and my day job, I decided to take my aspirations to the next level and attend University. I never looked back.

\n

I am a recovering O’Reilly book junkie who still has serious relapses from time-to-time. I try to stay open to any new software techniques and languages that come along, but currently I use PHP, Javascript, CSS, MySQL for most of my web application development, and Java for most of my desktop and mobile application development.

\n

I founded a few open source projects, including Xataface (a framework for building data-driven web applications with PHP and MySQL) and SWeTE (Simple Website Translation Engine), a proxy for internationalizing web applications. I also enjoy blogging about software-related issues.

" }, - + { - + "serial": 140339, "name": "Harold Hannon", "photo": null, @@ -10228,9 +10228,9 @@ "twitter": "", "bio": "

Harold Hannon has been working in the field of software development as both an Architect and Developer for over 15 years, with a focus on workflow, integration and distributed systems. He is currently a Sr. Software Architect at SoftLayer Technologies working within their Product Innovation team. Harold has a passion for leveraging open source solutions to bring real value to the Enterprise space, and has implemented open source solutions with many companies across the globe. Harold is also active in mobile application development, with multiple published applications.

" }, - + { - + "serial": 132865, "name": "Scott Hanselman", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_132865.jpg", @@ -10240,9 +10240,9 @@ "twitter": "shanselman", "bio": "

Scott is a web developer who has been blogging at http://hanselman.com for over a decade. He works on Azure and ASP.NET for Microsoft out of his home office in Portland. Scott has three podcasts, http://hanselminutes.com for tech talk, http://thisdeveloperslife.com on developers’ lives and loves, and http://ratchetandthegeek.com for pop culture and tech media. He’s written a number of books and spoken in person to almost a half million developers worldwide.

" }, - + { - + "serial": 86090, "name": "Jonah Harris", "photo": null, @@ -10252,9 +10252,9 @@ "twitter": "oracleinternals", "bio": "

Jonah has administered, developed against, and consulted on every major commercial and open source database system to date; his range of knowledge includes everything from query language specifics to the details of Oracle, EnterpriseDB, PostgreSQL, MySQL, SAP DB, Firebird, Ingres, and InnoDB internals, file formats, and network protocols.

" }, - + { - + "serial": 152118, "name": "Adam Harvey", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_152118.jpg", @@ -10264,9 +10264,9 @@ "twitter": "LGnome", "bio": "

Adam is a PHP Agenteer (it’s totally a word) at New Relic who is celebrating his 20th year of swearing at browsers that refuse to do his bidding. In between said bouts of invective, Adam works on various open source projects, including PHP, and attempts to figure out the great mysteries of life (well, the cricket related ones, at least).

" }, - + { - + "serial": 8837, "name": "Leslie Hawthorn", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_8837.jpg", @@ -10276,9 +10276,9 @@ "twitter": "lhawthorn", "bio": "

An internationally known community manager, speaker and author, Leslie Hawthorn has spent the past decade creating, cultivating and enabling open source communities. She created the world\u2019s first initiative to involve pre-university students in open source software development, launched Google\u2019s #2 Developer Blog, received an O\u2019Reilly Open Source Award in 2010 and gave a few great talks on many things open source.

\n

In August 2013, she joined Elasticsearch as Community Manager, where she leads Developer Relations. She works from Elasticsearch’s EU HQ in Amsterdam, The Netherlands – when not out and about gathering user praise and pain points. You can follow her adventures on Twitter

" }, - + { - + "serial": 104522, "name": "Steve Heffernan", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_104522.jpg", @@ -10288,9 +10288,9 @@ "twitter": "heff", "bio": "

Steve Heffernan is the creator of Video.js, an open source web video player in use on over 100,000 websites and with over 1 billion views per month. Steve was previously co-founder of Zencoder, a cloud video encoding service that was acquired by Brightcove. As part of Brightcove, Steve now works full-time managing the Video.js project and community.

" }, - + { - + "serial": 108520, "name": "Christopher Helm", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_108520.jpg", @@ -10300,9 +10300,9 @@ "twitter": "", "bio": "

Javascript Engineer / solver of geospatial problems

" }, - + { - + "serial": 171372, "name": "Sam Helman", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171372.jpg", @@ -10312,9 +10312,9 @@ "twitter": "", "bio": "

Sam has been working as a software engineer at MongoDB since August 2012. Before that, he got his degree in Computer Science at Brown University, where in addition to spending huge amounts of time programming he made films, did comedy, and played music and soccer.

" }, - + { - + "serial": 182521, "name": "Raymond Henderson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_182521.jpg", @@ -10324,9 +10324,9 @@ "twitter": "", "bio": "

Ray has over 25 years of experience in the business and non-profit sector. He is the Executive Director of Grassroots.org, a nonprofit organization that helps over 8500 nonprofits accelerate their efforts through the use of technology and serves on the Board of Directors for Fight the New Drug.org. Previously, he served as the Executive Director of Ohana Makamae Inc, an award winning substance abuse clinic and as Board President of Ma Ka Hana Ka `Ike, a nationally recognized, building and trades program for \u201cat risk\u201d youth in Hawaii. Prior to his work with non-profits, he worked marketing management for US West Direct, MCI WorldCom and MCA Records. His awards include: 2009 State of Hawaii \u2013 Governor\u2019s Innovation Award for Nonprofits, 2008 Maui- Executive Director of the Year, 2005 Outstanding Community Service Award and 2004 State of Hawaii – Collaborative Nonprofit Leader of the Year.

" }, - + { - + "serial": 173093, "name": "Ben Henick", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173093.jpg", @@ -10336,9 +10336,9 @@ "twitter": "bhenick", "bio": "

Ben Henick has worked as a freelance web developer for more than fifteen years, catering especially to SMBs with unique project requirements. He has also fulfilled a number of production contracts relating to sites well-known to technology workers; odds are short that at one time or another, your web browser has rendered a stylesheet that he wrote.

\n

Ben has fulfilled one contract for O’Reilly Media, for HTML & CSS: The Good Parts (2010). He is currently working on three others:

\n
    \n\t
  • The New Beginner’s Guide to HTML (\u2026And CSS) (2014, Atlas-only, free-to-read)
  • \n\t
  • No-Nonsense HTML & CSS (2014)
  • \n\t
  • Mastering HTML & CSS (2014)
  • \n
\n

Prior to joining O’Reilly’s author roster, Ben served a stint as the Managing Editor of the erstwhile Digital Web Magazine and was active in several volunteer professional education settings, including the Web Standards Project, Evolt, and webdesign-l. He still lurks webdesign-l and the css-d mailing lists, along with other O’Reilly authors.

\n

Ben lives in his hometown of Portland, Oregon, and is currently seeking to switch from freelance to fulltime exempt work.

" }, - + { - + "serial": 161475, "name": "Cody Herriges", "photo": null, @@ -10348,9 +10348,9 @@ "twitter": "cody_herriges", "bio": "

Cody Herriges joined Puppet Labs in 2010 and now serves as the current Tech Lead of Systems maintaining Puppet Labs’ public and private cloud platforms, storage, and hardware infrastructure. He started his career in higher education infrastructure management where he deployed his school’s first x86 virtualization cluster on top of KVM. It was a world of complicated legacy software needing to fit into a transformed open source world. Needing to maintain all this software eventually taught him the importance of automation.

" }, - + { - + "serial": 156534, "name": "Jason Hibbets", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_156534.jpg", @@ -10360,9 +10360,9 @@ "twitter": "jhibbets", "bio": "

Jason Hibbets is a project manager in Corporate Marketing at Red Hat. He is the lead administrator, content curator, and community manager for Opensource.com and has been with Red Hat since 2003. He is the author of “The foundation for an open source city,” a book that outlines how to implement the open source city brand through open government.

\n

He graduated from NC State University and lives in Raleigh, NC. where he’s been applying open source principles in neighborhood organizations in Raleigh for several years, highlighting the importance of transparency, collaboration, and community building. In his spare time, he enjoys surfing, gardening, watching football, participating in his local government, blogging for South West Raleigh, and training his Border Collies to be frisbee and agility dogs. He heads to the beaches of North Carolina during hurricane season to ride the waves.

" }, - + { - + "serial": 171822, "name": "Bridget Hillyer", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171822.jpg", @@ -10372,9 +10372,9 @@ "twitter": "BridgetHillyer", "bio": "

Bridget is an independent software consultant of many years. She is on the journey to become a Clojure programmer.

" }, - + { - + "serial": 6653, "name": "Mark Hinkle", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_6653.jpg", @@ -10384,9 +10384,9 @@ "twitter": "mrhinkle", "bio": "

Once upon a time, a lot longer ago then he wished Mark Hinkle was born in a small Pennsylvania town and through the most unlikely sequence of events ended up as a technologist involved in the development and evangelism of free and open source software. Mark has written extensively on open source as the former editor-in-chief of LinuxWorld Magazine and for numerous other publications (Network World, Forbes, Linux.com). He currently is the Senior Director, Open Source Solutions at Citrix Systems where he helps support the Apache CloudStack and Xen.org projects. You can find his blog at www.socializedsoftware.com and you can follow him on Twitter @mrhinkle.

" }, - + { - + "serial": 122917, "name": "Rob Hirschfeld", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_122917.jpg", @@ -10396,9 +10396,9 @@ "twitter": "zehicle", "bio": "

With a background in Mechanical and Systems Engineering, Rob Hirschfeld, Sr. Distinguished Cloud Architect at Dell, specializes in operations for large scale, integrated, and innovative cloud systems. He’s a community elected OpenStack board member, a leader of Dell’s OpenStack efforts, and founder of the Crowbar project. Rob helps Dell to set strategy for cloud computing, drives innovative cloud solutions to market, and works with customers on their cloud implementations. Rob is a graduate of Duke and Louisiana State University. In addition to cloud technologies, Rob is also known for his passion and expertise on the Agile/Lean process. You can find Rob\u2019s thoughts on cloud innovation at his blog RobHirschfeld.com or as @Zehicle. on Twitter.

" }, - + { - + "serial": 62631, "name": "Jeanne Holm", "photo": null, @@ -10408,9 +10408,9 @@ "twitter": "jeanne_jpl", "bio": "

Jeanne Holm is the Chief Knowledge Architect at the Jet Propulsion Laboratory (JPL), California Institute of Technology. Ms. Holm leads NASA\u2019s Knowledge Management Team, looking at how to access and use the knowledge gathered over the many missions of the US space agency to support missions and to drive innovation. As a lead for the award-winning NASA public and internal portals, she was at the helm of NASA\u2019s web during the largest Internet event in Government history\u2014the landing of the Mars Exploration Rovers on the surface of Mars. As the lead implementer for technologies supporting project managers at NASA, her team\u2019s solutions are helping to drive how people will manage space missions in the future, learn virtually, and share lessons learned. Her latest activities involve the transformation of NASA into a learning organization through innovative techniques in developing communities of practice and ensuring lessons are shared and embedded across the organization. Ms. Holm chairs The Federal Knowledge Management Group and a United Nations group looking at KM for Space.

\n

Her degrees are from UCLA and Claremont Graduate University. She is an instructor at UCLA where she teaches internationally and her online and ground-based courses focuses on KM strategies and social networking. She has been awarded numerous honors, including the NASA Exceptional Service Medal for leadership (twice), the NASA Achievement Award for her work on the Galileo and Voyager spacecraft, three Webby\u2019s from The International Academy of Digital Arts and Sciences, Competia\u2019s 2003 Champion of the Year, and a best practice from the APQC for \u201cUsing Knowledge Management to Drive Innovation\u201d.

\n

Specialties
\nDesigning and implementing knowledge architectures, system design and integration, knowledge management practices and systems, knowledge-based engineering, e-learning, instructional design and delivery, social networking analysis, inter-agency and inter-organizational knowledge sharing

" }, - + { - + "serial": 173380, "name": "Johnny Hughes", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173380.jpg", @@ -10420,9 +10420,9 @@ "twitter": "", "bio": "

CentOS-4 Lead Developer, CentOS-5 updates, CentOS-6 updates. If you are looking for me in the CentOS IRC channels or Forums I am hughesjr there.

\n

I first started working with UNIX in the early 1980’s with an AT&T 3b2 server.

\n

I first started working with Linux in 1995 with a Red Hat Linux webserver.

\n

I retired from the US Navy in 1997 (I spent ~20 years as a Engineering Laboratory Technician on Nuclear Submarines while in the US Navy). Lots of that time I also spent as a UNIX systems administrator working with AT&T and HP-UX servers.

\n

Since 1997 I have been a UNIX (HP-UX, AIX, Solaris), Windows (NT, 2000, 2003), Linux (Red Hat, CentOS) systems administrator, an Oracle DBA, a Senior Systems Analyst and a Network Engineer … and finally now a Software Engineer for the CentOS Project

" }, - + { - + "serial": 77939, "name": "Nathan Humbert", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_77939.jpg", @@ -10432,9 +10432,9 @@ "twitter": "", "bio": "

Nathan Humbert is a Senior Software Engineer at New Relic where he works with an amazing group of people to build software that helps people understand how their software is functioning.

" }, - + { - + "serial": 159719, "name": "Michael Hunger", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_159719.jpg", @@ -10444,9 +10444,9 @@ "twitter": "mesirii", "bio": "

Michael Hunger has been passionate about software development for a long time. He is particularly interested in the people who develop software, software craftsmanship, programming languages, and improving code.

\n

For the last two years he has been working with Neo Technology on the Neo4j graph database. As the project lead of Spring Data Neo4j he helped developing the idea to become a convenient and complete solution for object graph mapping. He is also taking care of Neo4j cloud hosting efforts.

\n

As a developer he loves to work with many aspects of programming languages, learning new things every day, participating in exciting and ambitious open source projects and contributing to different programming related books. Michael is a frequent speaker at industry events and is an active editor and interviewer at InfoQ.

" }, - + { - + "serial": 164756, "name": "Pete Hunt", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_164756.jpg", @@ -10456,9 +10456,9 @@ "twitter": "floydophone", "bio": "

Hacker at Facebook, currently leading Instagram.com web engineering. Formerly Facebook Photos and Videos lead.

\n

Core team on http://facebook.github.io/react/ and http://github.com/facebook/huxley

" }, - + { - + "serial": 4265, "name": "Kirsten Hunter", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_4265.jpg", @@ -10468,9 +10468,9 @@ "twitter": "synedra", "bio": "

Kirsten Hunter is an unapologetic hacker and passionate advocate for the development community. Her technical interests range from graph databases to cloud services, and her experience supporting and evangelizing REST APIs has given her a unique perspective on developer success. In her copious free time she’s a gamer, fantasy reader, and all around rabble-rouser. Code samples, recipes, and philosophical musings can be found on her blog, Princess Polymath.

" }, - + { - + "serial": 152299, "name": "Vanessa Hurst", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_152299.jpg", @@ -10480,9 +10480,9 @@ "twitter": "DBNess", "bio": "

Vanessa is a data-focused developer and the CEO of CodeMontage, which empowers coders to improve their impact on the world. She believes computing is one of the most efficient and effective ways to improve the human experience. She founded Developers for Good and co-founded WriteSpeakCode and Girl Develop It. Previously, she wrangled data at Paperless Post, Capital IQ, and WealthEngine. Vanessa holds a B.S. in Computer Science with a minor in Systems and Information Engineering from the University of Virginia.

\n

Vanessa’s work in technology education and social change has appeared on the TODAY show, NPR, Al Jazeera America, Entrepreneur, The New York Times, Fast Company, and other media. She serves as a cast member for Code.org, a nonprofit dedicated to ensuring every student has the opportunity to learn to code, which reached over 20 million students in 2013 alone.

" }, - + { - + "serial": 173235, "name": "Mihail Irintchev", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173235.jpg", @@ -10492,9 +10492,9 @@ "twitter": "irintchev", "bio": "

Born and living in Sofia, Bulgaria

\n

Lived in Ohio for a while

\n

Bachelor’s degree in CS from the American University in Bulgaria, class of 2003

\n

MSc from Sofia University – Electronic Business, class of 2006

\n

Web developer since 2003

\n

Co-founder of the BulgariaPHP user group

\n

Working at SiteGround since 2003

\n

Interested in history, military vehicles, motorcycles, science fiction, rock’n’roll

" }, - + { - + "serial": 122564, "name": "Phil Jackson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_122564.jpg", @@ -10504,9 +10504,9 @@ "twitter": "", "bio": "

Phil Jackson is Development Community Advocate for SoftLayer. He helps customers and partners integrate with the SoftLayer’s API. He also architects the company’s Drupal websites, writes API documentation, and maintains the developer blog. Formerly, he was a Sales Engineer building internal tools and providing technical consultation for potential and existing customers. Jackson started his career in webhosting at Ev1Servers where he led the training department. With a passion for technology that started at a young age, he has developed skills in a variety of scripting and programming languages and enjoys sharing his knowledge with the tech community.

" }, - + { - + "serial": 173189, "name": "Daniel Jacobson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173189.jpg", @@ -10516,9 +10516,9 @@ "twitter": "daniel_jacobson", "bio": "

Daniel Jacobson is the Director of Engineering for the API and Playback Services at Netflix, responsible for delivering more than one billion streaming hours a month to Netflix users on more than 1,000 device types. Prior to Netflix, he was Director of Application Development at NPR, leading the development of NPR\u2019s custom content management system. He also created NPR\u2019s API which became the distribution channel for getting NPR content to mobile platforms, member station sites throughout the country as well as to the public developer community. Daniel also co-authored the O\u2019Reilly book, \u201cAPIs: A Strategy Guide.

" }, - + { - + "serial": 173435, "name": "Paul Kehrer", "photo": null, @@ -10528,9 +10528,9 @@ "twitter": "reaperhulk", "bio": "

Paul Kehrer is the crypto expert on the Barbican project, an open source key management platform for OpenStack. He has experience running a public certificate authority as well as doing significant open source work in cryptography by writing and maintaining r509, a ruby library for managing a certificate infrastructure and cryptography, a modern python crypto library.

" }, - + { - + "serial": 120025, "name": "Benjamin Kerensa", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_120025.jpg", @@ -10540,9 +10540,9 @@ "twitter": "bkerensa", "bio": "

Benjamin Kerensa is an internationally recognized open source evangelist, community manager, writer and speaker who currently is a Firefox Release Manager and Community Manager for Trovebox.

" }, - + { - + "serial": 182019, "name": "Saud Khan", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_182019.jpg", @@ -10552,9 +10552,9 @@ "twitter": "", "bio": "

Over the past years Saud has worked on upgrading the user experience of Twitter for Android including search, trends and collections. From bringing search suggestions to the amazing photo grid of search results, Saud has worked with the designers to bring advanced search features in a mix of Twitter and Android design paradigm.

\n

Before that Saud developed a generic call management library used to bring VoIP capabilities in various mobile devices including Android, iOS and Windows CE. Additionally he worked on porting the solution over to myriad of embedded platforms like radio gateways and call boxes.

" }, - + { - + "serial": 157931, "name": "Shashank Khandelwal", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_157931.jpg", @@ -10564,9 +10564,9 @@ "twitter": "gazeti", "bio": "

As a Design and Technology Fellow at the Consumer Financial Protection Bureau, Shashank Khandelwal develops software to make consumer financial products work for the American people. He have over eight years of software development experience and his career spans multiple domains, languages and technologies. More recently, Shashank used a data-driven approach to study human health behavior. He wrote software to collect large-scale social media data, filter, and analyze it using machine learning techniques, and released it as a disease surveillance platform for the academic, private, and government health community. One of his earlier Twitter-based studies has been featured on NPR (“What Twitter Knows about Flu” by Jordan Calmes on October 14, 2011). Previously, he wrote document management software and worked on the vacation packages team at Orbitz (in Chicago). Over the years, he has written software in Python, Java, PHP and other languages using a variety of tools, applications and libraries.

" }, - + { - + "serial": 172929, "name": "Kevin Kluge", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172929.jpg", @@ -10576,9 +10576,9 @@ "twitter": "kevinkluge", "bio": "

Kevin is Vice President of Engineering at Elasticsearch, which produces open source search and analytics software. Previously Kevin was at Citrix Systems where he was responsible for engineering of Citrix CloudPlatform, powered by Apache CloudStack. Kevin has also held senior engineering positions at several other start-ups, including Cloud.com and Zimbra, and Sun Microsystems. Kevin has a B.S. and M.S. in Computer Science from Stanford University.

" }, - + { - + "serial": 144736, "name": "Francesca Krihely", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_144736.jpg", @@ -10588,9 +10588,9 @@ "twitter": "francium", "bio": "

Francesca is the Community Lead for MongoDB, managing the fastest growing community in Big Data. In this role she is responsible for managing acquisition and development of new community members and build out open source contributions to MongoDB’s server and project ecosystem. She holds a BA from Oberlin College in History and Sociology and is a recovering Philosophy nerd.

" }, - + { - + "serial": 125113, "name": "Spencer Krum", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_125113.jpg", @@ -10600,9 +10600,9 @@ "twitter": "", "bio": "

Spencer is a DevOps Engineer at UTi Worldwide Inc, a logistics firm. He has been using Puppet for 4 years. He is one of the co-authors of Pro Puppet 2nd edition from Apress. He lives and works in Portland and enjoys cycling and Starcraft.

" }, - + { - + "serial": 6380, "name": "Bradley Kuhn", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_6380.jpg", @@ -10612,9 +10612,9 @@ "twitter": "bkuhn_ebb_org", "bio": "

Bradley M. Kuhn is a Director of FSF, President of the Software Freedom Conservancy, and won the O’Reilly Open Source Award at OSCON 2012. Kuhn began his work in the Free, Libre and Open Source Software (FLOSS) Movement as a volunteer in 1992, when he became an early adopter of the GNU /Linux operating system, and began contributing to various FLOSS projects. He worked during the 1990s as a system administrator and software developer for various companies, and taught AP Computer Science at Walnut Hills High School in Cincinnati. Kuhn’s non-profit career began in 2000, when he was hired by the Free Software Foundation. As FSF’s Executive Director from 2001-2005, Kuhn led FSF’s GPL enforcement, launched its Associate Member program, and invented the Affero GPL. From 2005-2010, Kuhn worked as the Policy Analyst and Technology Director of the Software Freedom Law Center. Kuhn holds a summa cum laude B.S. in Computer Science from Loyola University in Maryland, and an M.S. in Computer Science from the University of Cincinnati. His Master’s thesis (an excerpt from which won the Damien Conway Award for Best Technical Paper at this conference in 2000) discussed methods for dynamic interoperability of FLOSS languages. Kuhn has a regular blog and a microblog (@bkuhn on identi.ca).

" }, - + { - + "serial": 170822, "name": "Andreas Kunz", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_170822.jpg", @@ -10624,9 +10624,9 @@ "twitter": "aku_dev", "bio": "

Working as Software Developer and Architect at SAP since 2005, focusing on user interfaces.
\nMember of the UI5 (HTML/JS UI library created by SAP) development team since its inception and spending considerable time to work around browser bugs…

" }, - + { - + "serial": 62981, "name": "Lars Kurth", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_62981.jpg", @@ -10636,9 +10636,9 @@ "twitter": "lars_kurth", "bio": "

Lars Kurth had his first contact with the open source community in 1997 when he worked on various parts of the ARM toolchain. This experience led Lars to become a passionate open source enthusiast who worked with and for many open source communities over the past 15 years. Lars contributed to projects such as GCC, Eclipse, Symbian and Xen and became the open source community manager for Xen.org in 2011. Lars is an IT generalist with a wide range of skills in software development and methodology. He is experienced in leading and building engineering teams and communities, as well as constructing marketing, product management, and change programs impacting 1000s of users. He has 17 years of industry experience in the infrastructure, tools and mobile sectors working at ARM, Citrix, Nokia and the Symbian Foundation.

" }, - + { - + "serial": 177245, "name": "Shadaj Laddad", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_177245.jpg", @@ -10648,9 +10648,9 @@ "twitter": "", "bio": "

Shadaj is a 14 year old who loves to program. He has programmed in Logo, NXT Mindstorms, Ruby, Python, C, Java, and Scala\u2014his favorite. Shadaj hosts his projects on GitHub, and has an educational channel on Youtube. He has presented at Scala Days 2013, Scala Days 2012, and the Bay Area Scala Enthusiast group showing his Scala projects. Besides programming, he likes Math and Science. Shadaj is also an active member of his school community as Student Council President. He loves spreading a love of technology, and started TechTalks\u2014a program that brings guest speakers to share their knowledge and enthusiasm with students at his school. When not doing his school work or programming, he plays guitar, sitar, and games, some of which he created.

" }, - + { - + "serial": 172658, "name": "Michael Laing", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172658.jpg", @@ -10660,9 +10660,9 @@ "twitter": "", "bio": "

NYTimes; Systems Architect; 2011-present \u2014 Architect of nyt\u2a0da\u0431rik

\n

United Nations; IT Evangelist; 2002-2011 \u2014 Principal advisor to CIO

\n

Blue Note Technology; CEO etc; 1998-2002

\n
    \n\t
  • 2001 New England Web Design award
  • \n\t
  • 2000 Software and Information Industry Association Codie award
  • \n
\n

Harvard University Tech Eval Group; consultant; 1985-1998

\n

Education:

\n
    \n\t
  • Harvard Business School
  • \n\t
  • United States Military Academy
  • \n
\n

Personal:

\n
    \n\t
  • Sings
  • \n\t
  • Dances
  • \n
" }, - + { - + "serial": 173308, "name": "Alex Lakatos", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173308.jpg", @@ -10672,9 +10672,9 @@ "twitter": "lakatos88", "bio": "

Alex Lakatos is a Mozilla Representative and contributor to the Mozilla project for the past three years, based in Cluj-Napoca, the heart of Transylvania. He’s a JavaScript developer building on the open web, trying to push it’s boundaries every day. You can check out his github profile or get in touch on twitter. When he\u2019s not programming, he likes to travel the world, so it\u2019s likely you\u2019ll bump into him in an airport lounge.

" }, - + { - + "serial": 137347, "name": "James Lance", "photo": null, @@ -10684,9 +10684,9 @@ "twitter": "", "bio": "

James Lance is a developer at Bluehost.com. He has been developing in Perl for over 10 years. He is also an active member in the Utah Open Source community and Core Team member of the OpenWest Conference

\n

When he\u2019s not behind a computer you can usually find a yo-yo in his hand. He also enjoys Ham radio, camping, and various forms of BBQing (smoking in particular).

" }, - + { - + "serial": 116050, "name": "Yoav Landman", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_116050.jpg", @@ -10696,9 +10696,9 @@ "twitter": "yoavlandman", "bio": "

Yoav is the CTO of JFrog, the Artifactory Binary Repository creators, JavaOne 2011 and 2013 Duke Choice Awards winner; Yoav laid the foundation to JFrog’s flagship product in 2006 when he founded Artifactory as an open source project.
\nIn 2008 he co-founded JFrog where he leads the future of products like Artifactory and, more recently, Bintray.
\nPrior to starting up JFrog, Yoav was a software architect and consultant in the field of configuration management and server-side JVM applications.
\nYoav blogs at jfrog.org and java.net.

" }, - + { - + "serial": 172864, "name": "Markus Lanthaler", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172864.jpg", @@ -10708,9 +10708,9 @@ "twitter": "markuslanthaler", "bio": "

Markus has been programming since the age of 9. After a short journey with Perl, he quickly moved to PHP for most of his Web-related programming. He implemented his first PHP-based CMS more than 10 years ago (about the time of Drupal\u2019s first release), worked with Symfony when it was still Mojavi, and launched his first online shop in 2003. Since then he has been programming almost everything from microcontrollers on Smart Cards (in assembler) up to large-scale distributed systems. More recently Markus\u2019 focus shifted to large, distributed Web applications. He is one of the core designers of JSON-LD, the inventor of Hydra, and a W3C Invited Expert.

" }, - + { - + "serial": 183609, "name": "Walter `wxl` Lapchynski", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_183609.jpg", @@ -10720,9 +10720,9 @@ "twitter": null, "bio": "

Walter \u2665 everything about bikes & computers. He’s a Release Manager for Lubuntu & the Team Leader for Ubuntu Oregon. Bioinformatics looms in his future, as much as his obsession with Unicode allows\u2026

" }, - + { - + "serial": 182081, "name": "Chris Launey", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_182081.jpg", @@ -10732,9 +10732,9 @@ "twitter": "clauney", "bio": "

Chris Launey is the Director of Cloud Services & Architecture in the core
\narchitecture and engineering group at the Walt Disney Company. His
\norganization lives at the intersection of the quickly shifting demands of
\na media and entertainment company and the mission of a managed service
\nprovider to maintain a stable, scalable, and efficient technology
\nplatform. Chris team launched Disney\u00b9s first true on-demand IaaS offering
\nand built a service integration platform to cloudify more internal
\ntechnology services. His team is now building on that to make pubic
\nservices easier to broker and consume for the Walt Disney Company.

" }, - + { - + "serial": 151665, "name": "Mark Lavin", "photo": null, @@ -10744,9 +10744,9 @@ "twitter": "", "bio": "

Mark is a lead Python/Django developer and technical manager at Caktus Consulting Group in Carrboro, NC. He also runs a small homebrewing website written in Django called brewedbyus.com. He came to Python web development after a few years pricing derivatives on Wall Street. Mark maintains a number of open source projects primarily related to Django development and frequently contributes back to projects used by Caktus. When he isn’t programming, Mark enjoys spending time with his wife and daughter, brewing beer, and running.

" }, - + { - + "serial": 152106, "name": "Ed Leafe", "photo": null, @@ -10756,9 +10756,9 @@ "twitter": "EdLeafe", "bio": "

Ed has been a developer for over 20 years, and is one of the original developers involved with the creation of OpenStack. After leaving the Microsoft world over a decade ago he has been primarily a Python developer, and has spoken as several US PyCons, as well as PyCon Australia and PyCon Canada. He has been working with Rackspace for the past 6 years as a senior Python developer. He also throws a mean Frisbee.

" }, - + { - + "serial": 74565, "name": "Jonathan LeBlanc", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_74565.jpg", @@ -10768,9 +10768,9 @@ "twitter": "jcleblanc", "bio": "

Jonathan LeBlanc is an Emmy award winning software engineer, author of the O\u2019Reilly book “Programming Social Applications”, and the head of North American Developer Evangelism at PayPal. Specializing in user identity concepts, hardware to web interconnectivity, data mining techniques, as well as open source initiatives around social engagement, Jonathan works on the development of emerging initiatives towards building a more user-centric web.

" }, - + { - + "serial": 157509, "name": "Robert Lefkowitz", "photo": null, @@ -10780,9 +10780,9 @@ "twitter": "sharewaveteam", "bio": "

Robert “r0ml” Lefkowitz is the CTO at Sharewave, a startup building an investor management portal. This year, he was a resident at the winter session of Hacker School. He is a Distinguished Engineer of the ACM.

" }, - + { - + "serial": 124700, "name": "Janice Levenhagen-Seeley", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_124700.jpg", @@ -10792,9 +10792,9 @@ "twitter": "", "bio": "

Janice is the Executive Director of ChickTech. She has a BS in Computer Engineering from Oregon State University and an MBA from Willamette University. She believes strongly that the diversity and strengths that women can bring will push high tech to even more impressive heights. Her inspiration for creating ChickTech came from her own experiences in computer engineering and the realization that the percentage of women in engineering isn\u2019t going to get higher by itself.

" }, - + { - + "serial": 161517, "name": "Pernilla Lind", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_161517.jpg", @@ -10804,9 +10804,9 @@ "twitter": "p3rnilla", "bio": "

Pernilla is the Community Manager at Neo Technology, the company behind the open source project Neo4j. Pernilla loves to connect and spread the word of graphs and bring new ideas into life. She is an engaging speaker and teacher and hasthe mission in life to spread knowledge about technology.Pernilla spent some times in Kenya with the massai people for a year to build up their infrastructure and engage their community to do income generating activities. Before that she lived in Gambia to help up a womens organization.

\n

Pernilla is also an engaging member and organizer of Geek Girl Meetup, a network for women in tech in Sweden, where she plans and organizes conferences and meetups. She is always involved in different technoogy ecosystems and loves to meet new people and learn about new cool tech stuff. To be a role model and stand up for peoples right it\u2019s something Pernilla always do.

\n

Cats, people, philantrophy, graphs, open data, women in tech, innovation, crazy ideas and Doctor Who are subjects Pernilla can talk about forever.

" }, - + { - + "serial": 77469, "name": "Philip Lindsay", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_77469.jpg", @@ -10816,9 +10816,9 @@ "twitter": "RancidBacon", "bio": "

Philip Lindsay (also known as “follower” from rancidbacon.com ) writes documentation, creates code libraries, develops example projects and provides developer support for companies including Pebble Technology, SparkFun Electronics, Arduino and other clients.

\n

Tim O’Reilly once called Philip a “troublemaker” for his early Google Maps reverse engineering efforts.

\n

He has a particular interest in the areas where design, art, craft and technology intersect.

\n

Follow his project logs at Labradoc.

" }, - + { - + "serial": 173403, "name": "Steve Lipner", "photo": null, @@ -10828,9 +10828,9 @@ "twitter": "msftsecurity", "bio": "

Steve Lipner, Senior Director of Security Engineering Strategy, Microsoft\u2019s Trustworthy Computing Group

\n

As the senior director of security engineering strategy in Microsoft Corp.\u2019s Trustworthy Computing Group, Steve Lipner is responsible for Microsoft\u2019s Security Development Lifecycle team, including the development of programs that provide improved product security and privacy to Microsoft\u00ae customers. Additionally, Lipner is responsible for Microsoft\u2019s engineering strategies related to the company\u2019s End to End Trust initiative, aimed at extending Trustworthy Computing to the Internet.

\n

Lipner has more than 35 years experience as a researcher, development manager and general manager in information technology security, and is named as inventor on thirteen U.S. patents in the field of computer and network security. He holds both an S.B. and S.M. degree from the Massachusetts Institute of Technology, and attended the Harvard Business School\u2019s Program for Management Development.

" }, - + { - + "serial": 171564, "name": "Joshua Long", "photo": null, @@ -10840,9 +10840,9 @@ "twitter": "starbuxman", "bio": "

Josh Long is the Spring developer advocate at [Pivotal](http://gopivotal.com). Josh is the lead author on 4 books and instructor in one of Safari’s best-selling video series, all on Spring. When he’s not hacking on code, he can be found at the local Java User Group or at the local coffee shop. Josh likes solutions that push the boundaries of the technologies that enable them. His interests include cloud-computing, business-process management, big-data and high availability, mobile computing and smart systems. He blogs on [spring.io](https://spring.io/team/jlong) or on [his personal blog](http://joshlong.com) and [on Twitter (@starbuxman)](http://twitter.com/starbuxman).

" }, - + { - + "serial": 141574, "name": "Tim Mackey", "photo": null, @@ -10852,9 +10852,9 @@ "twitter": "XenServerArmy", "bio": "

Tim Mackey is a technology evangelist for XenServer and CloudStack within the Citrix Open Source Business Office and focused on server virtualization and cloud orchestration technical competencies. When he joined Citrix in 2003, he was an engineer and architect focused on low impact application performance monitoring. During his tenure at Citrix he has held roles in the XenServer team as an architect, consultant, product manager and product marketing.

" }, - + { - + "serial": 109140, "name": "Anil Madhavapeddy", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_109140.jpg", @@ -10864,9 +10864,9 @@ "twitter": "avsm", "bio": "

Dr. Anil Madhavapeddy is a Senior Research Fellow at Wolfson College and is based in the Cambridge University Computer Laboratory, investigating programming models for cloud computing. Anil was on the original team at Cambridge that developed the Xen hypervisor, and subsequently served as the senior architect and product director for XenSource/Citrix before returning to academia.

\n

Anil has a diverse background in industry at Network Appliance, Citrix, NASA, and Internet Vision. He is an active member of the open source development community with the OpenBSD operating system and more, and the co-chair of this year’s Commercial Uses of Functional Programming conference.

" }, - + { - + "serial": 173303, "name": "Rachel Magario", "photo": null, @@ -10876,9 +10876,9 @@ "twitter": "rachelmagario", "bio": "

Rachel Magario is an Assistive Technology Specialist at Pacer Simon Technology Center. As the first totally blind interaction designer, Rachel is known as a leader and visionary who has triumphed over adversity consistently throughout her life.

\n

Rachel has presented at SXSW Interactive and consulted for various companies and universities throughout the United States on accessibility. Rachel has an MBA with a concentration in Marketing and a Masters in Interaction design from the University of Kansas.

\n

Magario\u2019s dream is that usability and accessibility can be considered from the start of a project and not as an after thought. She believes this would open the door for access of information and for accessible tools. This shift would allow her and others to pursue their careers of choice and live with the dignity that should be the right of every human.

\n

Rachel has been involved with accessibility consulting and advocacy since the early 2000s. Throughout the years, Rachel served as an accessibility consultant to several university related projects and non-profit sites that were required to comply with section 508.

\n

Rachel soon realized through her experience that accessibility issues often involve problems of usability that affect anyone who accesses information. When Rachel started her Masters in Interaction Design, she experienced the lack of accessibility in the design tools that she was using as well as in end products coming out of these tools.
\nSince then, Rachel has made her mission to research and develop models and prototypes of accessible user experience. She enjoys working closely with designers and developers to ensure standards are met and to create awareness of the importance of accessible user experience.

" }, - + { - + "serial": 173324, "name": "Allison Mankin", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173324.jpg", @@ -10888,9 +10888,9 @@ "twitter": "", "bio": "

Allison is the Director of Verisign Labs, a research organization focusing on long-term evolution of the
\nInternet infrastructure and on open standards-based prototyping. She has been active in Internet engineering
\nand research for over 25 years, including having served at the Internet Engineering Task Force as an area
\ndirector for 10 of those years. She is best known having co-led the IPng Selection Process at IETF (long ago).
\nPast open source projects include open internet conferencing, multicast and IPv6. Her research and RFCs have been primarily in the areas of TCP and DNS and their security.

" }, - + { - + "serial": 118998, "name": "Jonathon Manning", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_118998.jpg", @@ -10900,9 +10900,9 @@ "twitter": "desplesda", "bio": "

Jon Manning is the co-founder of Secret Lab, an independent game development studio based in Hobart, Tasmania, Australia. He\u2019s worked on apps of all sorts, ranging from iPad games for children to instant messaging clients. He\u2019s a Core Animation demigod, and frequently finds himself gesticulating wildly in front of classes full of eager-to-learn iOS developers.

\n

Jon is currently writing “Mobile Game Development With Unity” and “iOS Game Development Cookbook”, both for O’Reilly, and is the co-author of the books “Learning Cocoa with Objective-C Third Edition” (O’Reilly, 2012 and 2014), “iPhone and iPad Game Development For Dummies” (Wiley, 2010) and “Unity Mobile Game Development For Dummies” (Wiley, 2011).

\n

Jon is the world\u2019s biggest Horse_ebooks fan.

" }, - + { - + "serial": 6931, "name": "Joshua Marinacci", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_6931.jpg", @@ -10912,9 +10912,9 @@ "twitter": "joshmarinacci", "bio": "

Ask me about HTML Canvas, mobile apps, and visual design. Or 3D printing and wearable computing. Or just ask me to rant about Java.

\n

Josh Marinacci is a blogger and co-author of “Swing Hacks” and “Building Mobile Apps with Java” for O’Reilly. He is currently a researcher for Nokia.

\n

He previously worked on webOS at Palm and JavaFX, Swing, NetBeans, and the Java Store at Sun Microsystems.

\n

Josh lives in Eugene, Oregon and is passionate about open source technology & great user experiences.

" }, - + { - + "serial": 173388, "name": "Elaine Marino", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173388.jpg", @@ -10924,9 +10924,9 @@ "twitter": "elaine_marino", "bio": "

Elaine Marino is a Ruby on Rails coder, marketer and entrepreneur with a 13-year background in strategic marketing and brand-building for Fortune 500 companies. Her ability to communicate effectively, manage large-scale projects, build brands and communities as well as web applications, puts her in a unique position within the software sector. There is a sweet-spot where technology enhances the lives of people and communities — that is where she thrives.

\n

She founded LadyCoders Boulder, a 2-day career career conference for 60 women software engineers, hosted at Google Boulder. LadyCoders Boulder provides tangible, real-world advice to women engineers to advance their careers.

" }, - + { - + "serial": 164144, "name": "Will Marshall", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_164144.jpg", @@ -10936,9 +10936,9 @@ "twitter": "wsm1", "bio": "

Will is responsible for setting the company\u2019s vision and and for architecting the company strategy. Previously, Will was a Scientist at NASA/USRA where he served as Co-Investigator for PhoneSat, Science Team member on the LCROSS and LADEE lunar missions. He led research projects in orbital space debris remediation. Will has published over 30 articles in scientific publications. Will received his Ph.D. in Physics from the University of Oxford and was a Postdoctoral Fellow at Harvard University.

" }, - + { - + "serial": 5199, "name": "Alex Martelli", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_5199.jpg", @@ -10948,9 +10948,9 @@ "twitter": "", "bio": "

Alex Martelli wrote “Python in a Nutshell” and co-edited
\n“Python Cookbook”. He’s a PSF member, and won the 2002 Activators’ Choice Award and the 2006 Frank Willison Award for contributions to the Python community. He works as Senior Staff Engineer for Google. You can read some PDFs and
\nwatch some videos of his past presentations.

" }, - + { - + "serial": 173465, "name": "Ritchie Martori", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173465.jpg", @@ -10960,9 +10960,9 @@ "twitter": "", "bio": "

Ritchie Martori is Node.js and Mobile developer at StrongLoop where he focuses on LoopBack, an open source mobile backend-as-a-service. He previously authored deployd, another mBaaS in use by over 20,000 developers to create their backend APIs, and is a contributor to Angular.js.

" }, - + { - + "serial": 17378, "name": "Matt May", "photo": null, @@ -10972,9 +10972,9 @@ "twitter": null, "bio": "

Matt May
\nAccessibility Evangelist, Open Source and Accessibility
\nAdobe

\n

Matt May is a developer, technologist, and accessibility advocate who is responsible for working internally and externally with Adobe product teams and customers to address accessibility in Adobe products, ensure interoperability with assistive technologies, and make customers aware of the many accessibility features that already exist in Adobe products.

\n

Prior to joining Adobe, May worked for W3C/WAI on many of the core standards in web accessibility, led the Web Standards Project\u2019s Accessibility Task Force, helped to architect one of the first online grocery sites, HomeGrocer.com, and co-founded Blue Flavor, a respected web and mobile design consultancy. He is a member of the W3C/WAI Web Content Accessibility Guidelines Working Group and co-edited the first JavaScript techniques document for WCAG 2.0 in 2001.

\n

May is an accomplished speaker, having presented at dozens of conferences including Web 2.0 Expo, WebVisions, SXSW Interactive, CSUN Conference on Technology and Persons with Disabilities, Podcast and Portable Media Expo, Web Design World Seattle, Gilbane CMS Conference, and the International World Wide Web Conference, to name just a few.

" }, - + { - + "serial": 142336, "name": "Steve Mayzak", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_142336.jpg", @@ -10984,9 +10984,9 @@ "twitter": "", "bio": "

Steve Mayzak heads up the Systems engineering team at Elasticsearch. Having been in the software industry for over 15 years he has worked as a Developer, Architect and most recently as a Systems engineering leader. Steve loves bringing cutting edge Open Source technology to customers large and small and helping them solve some of their biggest and most interesting challenges.

" }, - + { - + "serial": 182080, "name": "Caroline McCrory", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_182080.jpg", @@ -10996,9 +10996,9 @@ "twitter": "CloudOfCaroline", "bio": "

Caroline is Senior Director at Gigaom Research. Caroline has a background in network and security engineering, enterprise operations and has served at companies such as ServiceMesh, VMware, Cisco, EMC, Siemens, CSC, the BBC and Motorola. Most recently, Caroline was Head of Product at Piston Cloud Computing, an enterprise OpenStack software company.

" }, - + { - + "serial": 74852, "name": "Matthew McCullough", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_74852.jpg", @@ -11008,9 +11008,9 @@ "twitter": "matthewmccull", "bio": "

Matthew McCullough, Training Pioneer for GitHub, is an energetic 15 year veteran of enterprise software development, world-traveling open source educator, and co-founder of a US consultancy. All of these activities provide him avenues of sharing success stories of leveraging Git and GitHub. Matthew is a contributing author to the Gradle and Jenkins O’Reilly books and creator of the Git Master Class series for O’Reilly. Matthew regularly speaks on the No Fluff Just Stuff conference tour, is the author of the DZone Git RefCard, and is President of the Denver Open Source Users Group.

" }, - + { - + "serial": 172994, "name": "Chris McEniry", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172994.jpg", @@ -11020,9 +11020,9 @@ "twitter": "macmceniry", "bio": "

Chris “Mac” McEniry is a practicing sysadmin responsible for running a large ecommerce and gaming service. He’s been working and developing in an operational capacity for 15 years. In his free time, he builds tools and thinks about efficiency.

" }, - + { - + "serial": 152376, "name": "Patrick McFadin", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_152376.jpg", @@ -11032,9 +11032,9 @@ "twitter": null, "bio": "

Patrick McFadin is regarded as a foremost expert for Apache Cassandra and data modeling. As Chief Evangelist for Apache Cassandra and consultant working for DataStax, he has been involved in some of the biggest deployments in the world.

" }, - + { - + "serial": 183246, "name": "Patrick McGarry", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_183246.jpg", @@ -11044,9 +11044,9 @@ "twitter": "", "bio": "

Patrick is currently incarnated as a Director of Community at Red Hat working to spread the Ceph gospel. An experienced community manager, gamer, mischief maker, and all around geek, Patrick spent five years writing and managing Slashdot under the nomme du keyboard
\n‘scuttlemonkey.’ Patrick enthusiastically helps companies to understand and adopt Open Source ideals and continues to be a strong advocate of FOSS on the desktop and in the enterprise. He has strong feelings about tomatoes, longs for his deep, dark cave, and still
\nhates writing these bios.

" }, - + { - + "serial": 172824, "name": "Mark McLoughlin", "photo": null, @@ -11056,9 +11056,9 @@ "twitter": "markmc_", "bio": "

Mark McLoughlin is a consulting engineer at Red Hat and has spent over a decade contributing to and leading open source projects like GNOME, Fedora, KVM, qemu, libvirt, oVirt and, of course, OpenStack.

\n

Mark is a member of OpenStack’s technical committee and the OpenStack Foundation board of directors. He contributes mostly to Oslo, Nova and TripleO but will happily dive in to any project.

\n

Mark is responsible for Red Hat’s OpenStack technical direction from the CTO office.

" }, - + { - + "serial": 171621, "name": "Harrison Mebane", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171621.jpg", @@ -11068,9 +11068,9 @@ "twitter": "harrisonmebane", "bio": "

After several years spent on a PhD in theoretical physics, I recently entered the “big data” community to apply what I’ve learned. I work primarily in Python but am always looking for new tools. I work closely with data scientists and engineers skilled in machine learning and data pipelining.

" }, - + { - + "serial": 153566, "name": "Gian Merlino", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_153566.jpg", @@ -11080,9 +11080,9 @@ "twitter": null, "bio": "

Gian is a contributor to the Kafka, Storm, and Druid open source projects and a developer at Metamarkets. He previously worked at Yahoo!, where he was responsible for its worldwide server deployment and configuration management platform. He holds a BS in Computer Science from California Institute of Technology

" }, - + { - + "serial": 104652, "name": "Justin Miller", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_104652.jpg", @@ -11092,9 +11092,9 @@ "twitter": "incanus77", "bio": "

Justin Miller is mobile lead at Washington, DC-based Mapbox working on the future of open mapping. He created the SQLite-based MBTiles open map tile format, contributed the leading offline map implementation for iOS, wrote the Simple KML parser, contributes to the Mac version of the TileMill map-making studio, and does it all in the open here. He lives and works here in Portland.

" }, - + { - + "serial": 175488, "name": "Katie Miller", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_175488.jpg", @@ -11104,9 +11104,9 @@ "twitter": "codemiller", "bio": "

Katie Miller is an OpenShift Developer Advocate at Red Hat, where she tries to squeeze as many different programming languages into her workdays as possible. The former newspaper journalist loves language, whether code or copy, and will pedantically edit any text you let her touch. Katie is a Co-Counder of the Lambda Ladies group for women in functional programming and one of the organisers of the Brisbane Functional Programming Group.

" }, - + { - + "serial": 171147, "name": "Michael Minella", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171147.jpg", @@ -11116,9 +11116,9 @@ "twitter": "michaelminella", "bio": "

Michael Minella is a software engineer, teacher and author with over a decade of enterprise development experience. Michael was a member of the expert group for JSR-352 (java batch processing). He currently works for Pivotal as the project lead for the Spring Batch project as well as an instructor at DePaul University. Michael is the author of Pro Spring Batch from Apress and the popular Refcard JUnit and EasyMock.

\n

Outside of the daily grind, Michael enjoys spending time with his family and enjoys woodworking, photography, and InfoSec as hobbies.

" }, - + { - + "serial": 4378, "name": "Wade Minter", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_4378.jpg", @@ -11128,9 +11128,9 @@ "twitter": "minter", "bio": "

H. Wade Minter is the CTO and founding team member at TeamSnap, an application that makes managing sports teams and groups easy. He is also the ring announcer for a professional wrestling federation. The two may be related.

" }, - + { - + "serial": 181598, "name": "Manav Mishra", "photo": null, @@ -11140,9 +11140,9 @@ "twitter": "", "bio": "

Manav is currently the Director of Product for HP Helion where he leads the product team responsible for the developer platform and experience.

\n

Manav has been in several product and engineering leadership roles across the tech industry. Most recently, at Google, Manav led the product management team responsible for the core and enterprise products for Google Analytics. Prior to joining Google, Manav spent 10-years at Microsoft leading product teams responsible for Windows (shell user experience, search and file management, cloud integration and the developer platform), Live Mesh (SkyDrive), OneCare and machine-learning based safety and security protection in Outlook, Exchange, Hotmail and Internet Explorer. Manav started his career at Intel as a software engineer working on x86 instruction set optimizations and networking protocols for Linux. He also has several international publications and 50+ patents to his credit.

\n

Manav graduated with a M.S. in Electrical Engineering from Washington State University and is based in San Francisco.

" }, - + { - + "serial": 45968, "name": "Lorna Jane Mitchell", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_45968.jpg", @@ -11152,9 +11152,9 @@ "twitter": null, "bio": "

Lorna is an independent PHP consultant based in Leeds, UK. She leads the joind.in open source project, which provides a platform for real-time, public feedback at community events. She is an experienced event organiser and speaker herself, having hosted the Dutch PHP Conference and co-founded the PHP North West conference and user group. She has spoken at technical events across Europe and beyond, predominantly on technical topics around PHP and APIs, but also on topics around business, projects and open source. She regularly delivers technical training sessions and is also active as a mentor with PHPWomen.org. Author of the book PHP Master from Sitepoint, Lorna loves to write and is regularly published at a number of outlets including netmagazine and of course her own blog lornajane.net.

" }, - + { - + "serial": 173399, "name": "Eric Mittelette", "photo": null, @@ -11164,9 +11164,9 @@ "twitter": "ericmitt", "bio": "

Developer Evangelist since the year 2000 in Microsoft France, Eric animate sessions and keynotes about Microsoft development platform since the beginning of .NET. At this time Eric integrate open source software and framework in his work, and join recently Microsoft Open Technologies, Inc as a Senior Technical Evangelist. Passionate about code algorithm since the 90\u2019s, eric work with C, C++, C# and try to illustrate technologies breakout with simplicity and fun. His background as Agronomic researcher provide him, an different point of view and approach and a real vulgarization expertise.

" }, - + { - + "serial": 159772, "name": "Richard Mortier", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_159772.jpg", @@ -11176,9 +11176,9 @@ "twitter": "mort___", "bio": "

Richard Mortier is Horizon Transitional Fellow in Computer Science at the University of Nottingham. His research focuses on user-centred systems, investigating the challenges that arise when we design and deploy infrastructure technology with which real people must interact. Specific current projects include exokernels for secure high-performance multiscale computing (Mirage); infrastructure for building a market around privacy-preserving third-party access to personal data (Dataware); and novel approaches to deploying and managing personal network services. Prior to joining Nottingham he spent two years as a founder of Vipadia Limited designing and building the Clackpoint and Karaka real-time communications services (acquired by Voxeo Corp.), six years as a researcher with Microsoft Research Cambridge, and seven months as a visitor at Sprint ATL, CA. He received a Ph.D. from the Systems Research Group at the University of Cambridge Computer Laboratory, and a B.A. in Mathematics, also from the University of Cambridge.

" }, - + { - + "serial": 33987, "name": "Diane Mueller", "photo": null, @@ -11188,9 +11188,9 @@ "twitter": null, "bio": "

Diane Mueller is RedHat’s OpenShift Origin Open Source Community Manager. Diane has been designing and implementing products and applications embedded into mission critical systems at F500 corporations for over 20 years She is a thought leader in cloud computing, open source community building, and cat herding.

\n

.Follow her on twitter @pythondj
\n

" }, - + { - + "serial": 156427, "name": "Dan Muey", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_156427.jpg", @@ -11200,9 +11200,9 @@ "twitter": null, "bio": "

I have dedicated over ten years to cPanel and have loved every minute of it: I \u2665 cPanel! Before cPanel I spent ten years at various technology companies working on everything from server and database administration to front/back\u2013end web development and other programming. In my free time I enjoy playing music, time with my wife, and being an uncle.

" }, - + { - + "serial": 147840, "name": "Scott Murray", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_147840.jpg", @@ -11212,9 +11212,9 @@ "twitter": "alignedleft", "bio": "

Scott Murray is a code artist who writes software to create data visualizations and other interactive phenomena. His work incorporates elements of interaction design, systems design, and generative art. Scott is an Assistant Professor of Design at the University of San Francisco, where he teaches data visualization and interaction design. He is a contributor to Processing, and is author of the O’Reilly title “Interactive Data Visualization for the Web”.

" }, - + { - + "serial": 120866, "name": "Henri Muurimaa", "photo": null, @@ -11224,9 +11224,9 @@ "twitter": "henrimuurimaa", "bio": "

Henri Muurimaa, MSc. has been a professional developer and team leader since 1997. Over the years he has used many Java technologies and tools in a plethora of projects ranging from days to dozens of man-years. Lately he has been exploring building desktop-like web applications with Scala and Vaadin. He is a contributor to the Scaladin project and has been a member of the Vaadin team since 2002.

" }, - + { - + "serial": 173146, "name": "Chad Naber", "photo": null, @@ -11236,9 +11236,9 @@ "twitter": "", "bio": "

Chad is a data engineer with more than 12 years of progressive experience in leading, industry-recognized multi-national service organizations such as Intel, Nike and Edmunds.com. He has experience with leading edge technologies such as Redshift, SQL Server , Hadoop (both utilizing the streaming API and direct Java), and Hive with a baseline of experience in Agile data warehousing. Chad currently is working as a big data architect at Intel.

" }, - + { - + "serial": 143674, "name": "Rachel Nabors", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_143674.jpg", @@ -11248,9 +11248,9 @@ "twitter": "RachelNabors", "bio": "

Rachel Nabors is an interaction developer, award-winning cartoonist, and host of the Infinite Canvas Screencast. She deftly blends the art of traditional storytelling with digital media to \u201ctell better stories through code\u201d at her company Tin Magpie. You can catch her as @RachelNabors on Twitter and at rachelnabors.com.

" }, - + { - + "serial": 141881, "name": "David Nalley", "photo": null, @@ -11260,9 +11260,9 @@ "twitter": "ke4qqq", "bio": "

David is a recovering sysadmin with a decade of experience. He’s a committer on the Apache CloudStack (incubating) project, and a contributor to the Fedora Project.

" }, - + { - + "serial": 146540, "name": "Paco Nathan", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_146540.jpg", @@ -11272,9 +11272,9 @@ "twitter": "pacoid", "bio": "

O’Reilly author (Enterprise Data Workflows with Cascading) and a \u201cplayer/coach\u201d who’s led innovative Data teams building large-scale apps for 10+ yrs. Expert in machine learning, cluster computing, and Enterprise use cases for Big Data. Interests: Mesos, PMML, Open Data, Cascalog, Scalding, Python for analytics, NLP.

" }, - + { - + "serial": 109297, "name": "Wynn Netherland", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_109297.jpg", @@ -11284,9 +11284,9 @@ "twitter": "pengwynn", "bio": "

Wynn Netherland has been building the web for nearly twenty years. With a passion for API user experience, he’s a prolific creator and maintainer of Ruby API wrappers. He now spends his days hacking on the GitHub API and is author of several books, most recently Sass and Compass in Action (Manning 2013).

" }, - + { - + "serial": 109468, "name": "Christopher Neugebauer", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_109468.jpg", @@ -11296,9 +11296,9 @@ "twitter": "chrisjrn", "bio": "

Christopher is a Python programmer from Hobart, Tasmania. He\u2019s a Computer Science Honours graduate of the University of Tasmania, and he now works primarily as an Android developer. Working with Android means that his day job involves more Java than he\u2019d like. He has a strong interest in the development of the Australian Python Community \u2014 he is an immediate past convenor of PyCon Australia 2012 and 2013 in Hobart, and is a member of the Python Software Foundation.

\n

In his spare time, Christopher enjoys presenting on Mobile development at Open Source conferences, and presenting on Open Source development at Mobile conferences.

" }, - + { - + "serial": 130731, "name": "Deb Nicholson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_130731.jpg", @@ -11308,9 +11308,9 @@ "twitter": "", "bio": "

Deb Nicholson works at the intersection of technology and social justice. She has over fifteen years of non-profit management experience and got involved in the free software movement about five years ago when she started working for the Free Software Foundation. She is currently the Community Outreach Director for the Open Invention Network – the defensive patent pool built to protect Linux projects. She is also the Community Manager for GNU MediaGoblin, a brand new federated media hosting program. In her spare time, she serves on the board of OpenHatch, a small non-profit dedicated to identifying and mentoring new free software contributors with a particular interest in building a more diverse free software movement.

" }, - + { - + "serial": 171598, "name": "Niklas Nielsen", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171598.jpg", @@ -11320,9 +11320,9 @@ "twitter": "quarfot", "bio": "

Niklas is also a distributed systems engineer at Mesosphere and a committer in the Apache Mesos Open Source project. Before joining Mesosphere, Niklas worked at Adobe as a virtual machine and compiler engineer and completed his Master\u2019s degree doing supercomputer debugger design at Lawrence Livermore National Laboratory.

" }, - + { - + "serial": 140811, "name": "Kelley Nielsen", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_140811.jpg", @@ -11332,9 +11332,9 @@ "twitter": null, "bio": "

Kelley is a relatively new kernel developer, who is learning under the excellent guidance of mentors through the Gnome Outreach Program for Women. She is also a co-coordinator for Codechix, which organizes in-person events of all kinds for women and everybody. Before settling on the kernel, she wrote a collection of Linux screen saver demos, aptly titled Xtra Screen Hacks, and a few Android apps. She blogs here.

" }, - + { - + "serial": 173503, "name": "Jesse Noller", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173503.jpg", @@ -11344,9 +11344,9 @@ "twitter": "jessenoller", "bio": "

Jesse Noller is a long time Python community member, developer and has contributed to everything from distributed systems to front-end interfaces. He\u2019s passionate about community, developer experience and empowering developers everywhere, in any language to build amazing applications. He currently works for Rackspace as a Developer Advocate and open source contributor.

" }, - + { - + "serial": 76338, "name": "Sarah Novotny", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_76338.jpg", @@ -11356,9 +11356,9 @@ "twitter": "sarahnovotny", "bio": "

Sarah Novotny is a technical evangelist and community manager for NGINX. Novotny has run large scale technology infrastructures as a Systems Engineer and a Database administrator for Amazon.com and the ill fated Ads.com. In 2001, she founded Blue Gecko, a remote database administration company with two peers from Amazon. Blue Gecko, was sold to DatAvail in 2012. She\u2019s also curated teams and been a leader in customer communities focused on high availability web application and platform delivery for Meteor Entertainment and Chef.

\n

Novotny regularly talks about technology infrastructure and geek lifestyle. She is additionally a program chair for O’Reilly Media’s OSCON. Her technology writing and adventures as well as her more esoteric musings are found at sarahnovotny.com.

" }, - + { - + "serial": 173452, "name": "Tim Nugent", "photo": null, @@ -11368,9 +11368,9 @@ "twitter": "The_McJones", "bio": "

Tim Nugent pretends to be a mobile app developer, game designer, PhD student and now he even pretends to be an author (he co-wrote the latest update to \u201cLearning Cocoa with Objective-C\u201d for O\u2019Reilly). When he isn\u2019t busy avoiding being found out as a fraud, he spends most of his time designing and creating little apps and games he won\u2019t let anyone see. Tim spent a disproportionately long time writing this tiny little bio, most of which was trying to stick a witty sci-fi reference in, before he simply gave up. He is, obviously, an avid board game player.

" }, - + { - + "serial": 251, "name": "Tim O'Reilly", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_251.jpg", @@ -11380,9 +11380,9 @@ "twitter": "timoreilly", "bio": "

Tim O’Reilly is the founder and CEO of O’Reilly Media. His original business plan was “interesting work for interesting people,” and that’s worked out pretty well. He publishes books, runs conferences, invests in early-stage startups, urges companies to create more value than they capture, and tries to change the world by spreading and amplifying the knowledge of innovators.

\n

Tim is also a partner at O’Reilly AlphaTech Ventures, a founder and board member of Safari Books Online and Maker Media, and on the boards of Code for America and PeerJ.

" }, - + { - + "serial": 133624, "name": "Stephen O'Sullivan", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_133624.jpg", @@ -11392,9 +11392,9 @@ "twitter": "steveos", "bio": "

A leading expert on big data architecture and Hadoop, Stephen brings over 20 years of experience creating scalable, high-availability, data and applications solutions. A veteran of WalmartLabs, Sun and Yahoo!, Stephen leads data architecture and infrastructure.

" }, - + { - + "serial": 172986, "name": "Anne Ogborn", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172986.jpg", @@ -11404,9 +11404,9 @@ "twitter": "", "bio": "

Anne Ogborn is an avid SWI-Prolog fan, professional SWI-Prolog programmer, and sometimes contributor to the SWI-Prolog project.
\nShe uses SWI-Prolog to create games that create change for the OSU Wavicles, and to program advanced social robotics for Robokind.

" }, - + { - + "serial": 147649, "name": "Tim Palko", "photo": null, @@ -11416,9 +11416,9 @@ "twitter": "", "bio": "

Tim celebrates software development using many languages and frameworks, heeding less to past experience in choosing technologies. Spring MVC, Hibernate, Rails, .NET MVC, Django and the variety of languages that come with are in his L1 cache. Among other endeavors to keep him sharp, he currently provides coded solutions for the Software Engineering Institute at CMU.

\n

Tim received a B.S. in Computer Engineering in 2003 and resides in Pittsburgh, PA.

" }, - + { - + "serial": 26426, "name": "Rajeev Pandey", "photo": null, @@ -11428,9 +11428,9 @@ "twitter": null, "bio": "

Rajeev Pandey joined Hewlett-Packard in 1995. He has worked in a wide variety of R&D and IT positions within HP Labs, HP-IT and HP R&D prior to joining HP Cloud. He has spent the last few years using Agile methods to architect, develop, and deploy digital imaging and printing-related web services. He holds BS degrees in Computer Science and Mathematics from Montana Tech, MS and PhD degrees in Computer Science from Oregon State University, and is a senior member of the IEEE and ACM.

" }, - + { - + "serial": 113667, "name": "Manish Pandit", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_113667.jpg", @@ -11440,9 +11440,9 @@ "twitter": "lobster1234", "bio": "

A programmer at heart, I work with great teams to build great products. Over the last decade, I’ve worked with companies ranging from software, financials, to media and entertainment. Solving scale problems with leading innovations in the tech space has been an area of interest to me. From building APIs at E*Trade, IGN, and Netflix I’ve evolved both as a leader as well as an engineer focused on scalable yet flexible, and highly performant architectures.

\n

I am also active with the developer community via github, stackoverflow, meetups, and conferences.

\n

My slideshare has decks from my talks at various events. Currently I am working at Netflix as an Engineering Manager in the Streaming Platforms group, where my team builds APIs and tools around device metadata and partner products. Prior to Netflix I was Director of Engineering at IGN.com, where I helped build the next-gen API for the social and content platforms.

\n

Follow me on LinkedIn.

" }, - + { - + "serial": 173223, "name": "James Pannacciulli", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173223.jpg", @@ -11452,9 +11452,9 @@ "twitter": "_jpnc", "bio": "

James Pannacciulli has been an enthusiast and user of GNU/Linux and related software since 1997 and is a supporter of the free (libre) software movement. He holds an MA and BA in theoretical linguistics from UCLA and Rutgers universities, respectively, and is currently a Systems Engineer at Media Temple.

\n

James has presented on Bash at SCALE and UUASC in Los Angeles.

" }, - + { - + "serial": 172630, "name": "Soohong Park", "photo": null, @@ -11464,9 +11464,9 @@ "twitter": "", "bio": "

Dr. Soohong Daniel Park is a senior member of research staff in open source office, Samsung and currently leading Internet of Things open source project collaborated with various partners. He has been working on IPv6 over Low Power Sensor Networks in IETF and advanced research topics around IoT. Also, he is currently working in W3C as Advisor Board.

" }, - + { - + "serial": 180019, "name": "Sanjay Patil", "photo": null, @@ -11476,9 +11476,9 @@ "twitter": "", "bio": "

Sanjay Patil is a member of the Industry Standards & Open Source team at SAP Labs \u2013 Palo Alto, and is responsible for driving strategic Open Source programs as well as governance of SAP\u2019s outbound Open Source projects. He has over 14 years of experience with Industry Standards and Open Source projects. He has led critical industry-wide standardization initiatives such as OASIS Web Services Reliable Messaging. He currently serves as a Director on the Board of OASIS, a standards development organization, and represents SAP on Open Source foundations such as CloudFoundry. His areas of interest include application platform technologies, Big Data and Cloud.

" }, - + { - + "serial": 171197, "name": "Josh Patterson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171197.jpg", @@ -11488,9 +11488,9 @@ "twitter": "jpatanooga", "bio": "

Josh Patterson currently runs a consultancy in the Big Data Machine Learning space. Previously Josh worked as a Principal Solutions Architect at Cloudera and an engineer at the Tennessee Valley Authority where he was responsible for bringing Hadoop into the smartgrid during his involvement in the openPDC project. Josh is a graduate of the University of Tennessee at Chattanooga with a Masters of Computer Science where he did research in mesh networks and social insect swarm algorithms. Josh has over 15 years in software development and continues to contribute to projects such as Apache Mahout, Metronome, IterativeReduce, openPDC, and JMotif.

" }, - + { - + "serial": 118233, "name": "James Pearce", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_118233.jpg", @@ -11500,9 +11500,9 @@ "twitter": "jamespearce", "bio": "

James manages the open source program at Facebook. He’s a developer and writer with a special passion for the web, mobile platforms of all sorts, and helping developers explore their potential.

\n

James’ mobile projects include confess.js, WhitherApps, tinySrc, ready.mobi, Device Atlas, and mobiForge. Previously at Sencha, dotMobi, Argogroup and Ernst & Young, he has also written books on the mobile web for Wrox & Wiley. He’s easy to find at /jamesgpearce or http://tripleodeon.com

" }, - + { - + "serial": 64512, "name": "Shawn Pearce", "photo": null, @@ -11512,9 +11512,9 @@ "twitter": "", "bio": "

Shawn Pearce has been actively involved in Git since early 2006. Shawn is the original author of git-gui, a Tk based graphical interface shipped with git, and git fast-import, a stream based import system often used for converting projects to git. Besides being the primary author of both git-gui and git fast-import, Shawn’s opinion, backed by his code, has influenced many decisions that form the modern git implementation.

\n

In early 2006 Shawn also founded the JGit project, creating a 100% pure Java reimplementation of the Git version control system. The JGit library can often be found in Java based products that interact with Git, including plugins for Eclipse and NetBeans IDEs, the Hudson CI server, Apache Maven, and Gerrit Code Review, a peer code review system specially designed for Git. Today he continues to develop and maintain JGit, EGit, and Gerrit Code Review.

" }, - + { - + "serial": 180056, "name": "Harry Percival", "photo": null, @@ -11524,9 +11524,9 @@ "twitter": "hjwp", "bio": "

During his childhood Harry seemed to be doing everything right — learning to program BASIC on Thomson TO-7s (whose rubber keys went “boop” when you pressed them) and Logo on a Green-screen Amstrad PCW. Something went wrong as he grew up, and Harry wasted several years studying Economics, becoming a management consultant (shudder), and programming little aside from overcomplicated Excel spreadsheets.

\n

But in 2009 Harry saw the light, let his true geek shine once again, did a new degree in Computer Science, and was lucky enough to secure an internship with Resolver Systems, the London-based company that has since morphed into PythonAnywhere. Here he was inculcated into the cult of Extreme Programming (XP), and rigorous TDD. After much moaning and dragging of feet, he finally came to see the wisdom of the approach, and now spreads the gospel of TDD through beginner’s workshops, tutorials and talks, with all the passion of a recent convert.

\n

Harry is currently writing a book for O’Reilly, provisionally titled “Test-Driven Development of Web Applications with Python”. He is trying to persuade his editor to have the title changed to “Obey the Testing Goat!”.

" }, - + { - + "serial": 173381, "name": "Jim Perrin", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173381.jpg", @@ -11536,9 +11536,9 @@ "twitter": "BitIntegrity", "bio": "

I began my linux journey in 1999 with Mandrake 6.1 and migrated to RHL around the 7.2 release I found out about CentOS in 2004, and joined the project formally later that year. Over the years I’ve been a consultant for defense contractors as well as for the oil and gas industry, handling large scale deployment, automation, and systems integration.

\n

My current responsibilities with CentOS:

\n

Governing Board member
\nInfrastructure
\nSIG/Variant sponsor/coordinator

" }, - + { - + "serial": 151611, "name": "Jerome Petazzoni", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_151611.jpg", @@ -11548,9 +11548,9 @@ "twitter": "jpetazzo", "bio": "

Jerome is a senior engineer at Docker, where he rotates between Ops, Support and Evangelist duties. In another life he built and operated Xen clouds when EC2 was just the name of a plane, developed a GIS to deploy fiber interconnects through the French subway, managed commando deployments of large-scale video streaming systems in bandwidth-constrained environments such as conference centers, and various other feats of technical wizardry. When annoyed, he threatens to replace things with a very small shell script. His left hand cares for the dotCloud PAAS servers, while his right hand builds cool hacks around Docker.

" }, - + { - + "serial": 30412, "name": "Brandon Philips", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_30412.jpg", @@ -11560,9 +11560,9 @@ "twitter": "philips", "bio": "

Brandon Philips is helping to build modern Linux server infrastructure at CoreOS. Prior to CoreOS, he worked at Rackspace hacking on cloud monitoring and was a Linux kernel developer at SUSE. In addition to his work at CoreOS, Brandon sits on Docker’s governance board and is one of the top contributors to Docker. As a graduate of Oregon State’s Open Source Lab he is passionate about open source technologies.

" }, - + { - + "serial": 29591, "name": "Simon Phipps", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_29591.jpg", @@ -11572,9 +11572,9 @@ "twitter": "webmink", "bio": "

Simon Phipps has engaged at a strategic level in the world\u2019s leading technology companies, starting in roles such as field engineer, programmer, systems analyst and more recently taking executive leadership roles around open source. He worked with OSI standards in the 80s, on collaborative conferencing software in the 90s, helped introduce both Java and XML at IBM and was instrumental in open sourcing the whole software portfolio at Sun Microsystems.

\n

As President of the Open Source Initiative and a director of the UK’s Open Rights Group, he takes an active interest in digital rights issues and is a widely read commentator at InfoWorld, Computerworld and his own Webmink blog.

\n

He holds a BSc in electronic engineering and is a Fellow of the British Computer Society and of the Open Forum Academy.

" }, - + { - + "serial": 141661, "name": "Andy Piper", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_141661.jpg", @@ -11584,9 +11584,9 @@ "twitter": "andypiper", "bio": "

Andy Piper is a Developer Advocate for Cloud Foundry, the Open Source Platform-as-a-Service. He is an Eclipse M2M IWG Community Member and has been involved with the Eclipse Paho project since the start, particularly through his former role advocating the use of MQTT at IBM, and helping to run the mqtt.org community website. Andy has a passionate interest in Open Source, small and mobile devices, cloud, the Internet of Things, and Arduino and related technologies. He is probably best known online as a \u201csocial bridgebuilder\u201d. He was previously with IBM Software Group for more than 10 years, as a consultant, strategist, and WebSphere Messaging Community Lead.

" }, - + { - + "serial": 170158, "name": "Curtis Poe", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_170158.jpg", @@ -11594,11 +11594,11 @@ "position": "Freelance Perl expert and Agile consultant", "affiliation": "All Around The World", "twitter": "OvidPerl", - "bio": "

I’m a well-known Perl expert, better known online as Ovid. I specialize in large-scale, database driven code bases and wrote the test harness that currently ships with the Perl programming language. I’m constantly trying to create better testing tools for the Perl community.

\n

I sit on the Board of Directors of the Perl Foundation and run a consulting company with my lovely wife, Le\u00efla, from our offices in La Rochelle, a medieval port town on the west coast of France.

\n

I speak at conferences all over the world and also do private speaking engagements and training for companies. Currently I’m a specialist for hire, often focusing on complex ETL problems or making developers more productive by fixing test suites and making them run much faster.

" + "bio": "

I’m a well-known Perl expert, better known online as Ovid. I specialize in large-scale, database driven codebases and wrote the test harness that currently ships with the Perl programming language. I’m constantly trying to create better testing tools for the Perl community.

\n

I sit on the Board of Directors of the Perl Foundation and run a consulting company with my lovely wife, Le\u00efla, from our offices in La Rochelle, a medieval port town on the west coast of France.

\n

I speak at conferences all over the world and also do private speaking engagements and training for companies. Currently I’m a specialist for hire, often focusing on complex ETL problems or making developers more productive by fixing test suites and making them run much faster.

" }, - + { - + "serial": 171078, "name": "Jess Portnoy", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171078.jpg", @@ -11608,9 +11608,9 @@ "twitter": "jess_port01", "bio": "

Jess Portnoy has been an open source developer and believer for the last 15 years. Prior to Kaltura, Jess worked, among other places, at Zend where she was responsible for porting and packaging PHP and Zend projects on all supported .*nix platforms. Jess also grows 3 pet projects hosted in sourceforge.

\n

You can view my LinkedIn profile here.

" }, - + { - + "serial": 142320, "name": "Steven Pousty", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_142320.jpg", @@ -11620,9 +11620,9 @@ "twitter": "TheSteve0", "bio": "

Steve is a PaaS Dust Spreader (aka developer evangelist) with OpenShift. He goes around and shows off all the great work the OpenShift engineers do. He can teach you about PaaS with Java, Python, PostgreSQL MongoDB, and some JavaScript. He has deep subject area expertise in GIS/Spatial, Statistics, and Ecology. He has spoken at over 50 conferences and done over 30 workshops including Monktoberfest, MongoNY, JavaOne, FOSS4G, CTIA, AjaxWorld, GeoWeb, Where2.0, and OSCON. Before OpenShift, Steve was a developer evangelist for LinkedIn, deCarta, and ESRI. Steve has a Ph.D. in Ecology from University of Connecticut. He likes building interesting applications and helping developers create great solutions.

" }, - + { - + "serial": 173111, "name": "Mark Powell", "photo": null, @@ -11632,9 +11632,9 @@ "twitter": "drmarkpowell", "bio": "

Mark Powell is a Senior Computer Scientist at the Jet Propulsion Laboratory, Pasadena, CA since 2001. Mark is the product lead for the Mars Science Laboratory mission science planning interface (MSLICE). At JPL his areas of focus are science data visualization and science planning for telerobotics. He received the 2004 NASA Software of the Year Award for his work on the Science Activity Planner science visualization and activity planning software used for MER operations. Mark is currently supporting a variety of projects at JPL including Opportunity and Curiosity rover operations and radar data processing for volcanology and seismology research.

" }, - + { - + "serial": 155107, "name": "Austin Putman", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_155107.jpg", @@ -11644,9 +11644,9 @@ "twitter": "austinfrmboston", "bio": "

I empower people to make a difference with appropriate technology. Currently I’m working to turn around the diabetes epidemic.

\n

I was a founding partner and lead dev at Radical Designs, a tech cooperative for nonprofits, and a team anchor for Pivotal Labs, training and collaborating with earth’s best agile engineers.

" }, - + { - + "serial": 173467, "name": "Steven Quella", "photo": null, @@ -11656,9 +11656,9 @@ "twitter": "", "bio": "

Steven is a senior at Saint Joseph’s College, majoring in Computer Science. He is seeking a position after he graduates as a Java developer.

" }, - + { - + "serial": 151833, "name": "Dave Quigley", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_151833.jpg", @@ -11666,11 +11666,11 @@ "position": "Computer Science Professional", "affiliation": "KEYW Corporation", "twitter": "", - "bio": "

David Quigley making a return appearance to OSCON after his \u201cDemystifying SELinux: WTF is it saying?\u201d talk started his career as a Computer Systems Researcher for the National Information Assurance Research Lab at the NSA where he worked as a member of the SELinux team. David leads the design and implementation efforts to provide Labeled-NFS support for SELinux. David has previously contributed to the open source community through maintaining the Unionfs 1.0 code base and through code contributions to various other projects. David has presented at conferences such as the Ottawa Linux Symposium, the StorageSS workshop, LinuxCon and several local Linux User Group meetings where presentation topics have included storage, file systems, and security. David currently works as a Computer Science Professional for the Operations, Analytics and Software Development (OASD) Division at Keyw Corporation.

" + "bio": "

David Quigley making a return appearance to OSCON after his \u201cDemystifying SELinux: WTF is it saying?\u201d talk started his career as a Computer Systems Researcher for the National Information Assurance Research Lab at the NSA where he worked as a member of the SELinux team. David leads the design and implementation efforts to provide Labeled-NFS support for SELinux. David has previously contributed to the open source community through maintaining the Unionfs 1.0 codebase and through code contributions to various other projects. David has presented at conferences such as the Ottawa Linux Symposium, the StorageSS workshop, LinuxCon and several local Linux User Group meetings where presentation topics have included storage, file systems, and security. David currently works as a Computer Science Professional for the Operations, Analytics and Software Development (OASD) Division at Keyw Corporation.

" }, - + { - + "serial": 161577, "name": "Carl Quinn", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_161577.jpg", @@ -11680,9 +11680,9 @@ "twitter": null, "bio": "

Carl Quinn has been developing software professionally for 34 years, starting with BASIC on an Apple II, slogging through C/C++ on DOS, Windows and embedded, and finally landing in the Java-on-Linux world. The one thread through his career has been an inexplicable attraction to developer tools, spending time building them at Borland (C++ & Java IDEs), Sun (Java RAD), Google (Java & C++ build system), Netflix (Java build and cloud deployment automation) and most recently at Riot Games (Cloud Architect). Carl also co-hosts the Java Posse podcast, the #1 ranked Java technology podcast.

" }, - + { - + "serial": 182741, "name": "Kate Rafter", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_182741.jpg", @@ -11692,9 +11692,9 @@ "twitter": "", "bio": "

As a choreographer and interdisciplinary performance artist, Kate Rafter has presented work at the Edinburgh Festival Fringe, Scotland\u2019s Dance Base, Portland’s Fertile Ground Festival, Conduit, American College Dance Festival NW, the Dance Coalition of Oregon, (a)merging 2014, and the Someday:Incubator. She heads Automal, a project-based indie dance company.

\n

Kate’s performing arts ambitions go beyond dance and theatre conventions to include audience interactivity, immersive environments, and an intent to break every wall, starting with the fourth.

" }, - + { - + "serial": 173432, "name": "Jarret Raim", "photo": null, @@ -11704,9 +11704,9 @@ "twitter": "jarretraim", "bio": "

Jarret Raim is the Security Product Manager at Rackspace Hosting. Since joining Rackspace, he has built a software assurance program for Rackspace\u2019s internal software teams as well as defined strategy for building secure systems on Rackspace\u2019s OpenStack Cloud implementation. Through his experience at Rackspace, and as a consultant for Denim Group, Jarret has assessed and remediated applications in all industries and has experience width a wide variety of both development environments and the tools used to audit them. Jarret has recently taken charge of Rackspace’s efforts to secure the Cloud through new product development, training and research. Jarret holds a Masters in Computer Science from Lehigh University and Bachelors in Computer Science from Trinity University.

" }, - + { - + "serial": 150170, "name": "Luciano Ramalho", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_150170.jpg", @@ -11716,9 +11716,9 @@ "twitter": "ramalhoorg", "bio": "

Luciano Ramalho was a Web developer before the Netscape IPO in 1995, and switched from Perl to Java to Python in 1998. Since then he worked on some of the largest news portals in Brazil using Python, and taught Python web development in the Brazilian media, banking and government sectors. His speaking credentials include OSCON 2013 (slides) and 2002, two talks at PyCon USA 2013 and 17 talks over the years at PythonBrasil (the Brazilian PyCon), FISL (the largest FLOSS conference in the Southern Hemisphere) and a keynote at the RuPy Strongly Dynamic Conference in Brazil. Ramalho is a member of the Python Software Foundation and co-founder of Garoa Hacker Clube, the first hackerspace in Brazil. He is a managing partner at Python.pro.br, a training company.

" }, - + { - + "serial": 3471, "name": "Anna Martelli Ravenscroft", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_3471.jpg", @@ -11728,9 +11728,9 @@ "twitter": "annaraven", "bio": "

Anna Martelli Ravenscroft has a background in training and mentoring. Her focus is on practical, real-world problem solving and the benefits of diversity and accessibility. Anna graduated in 2010 from Stanford University with a degree in Cognitive Science. She is a member of the Python Software Foundation, a program committee member for several open source conferences, winner of the 2013 Frank Willison Award, and co-edited the Python Cookbook 2nd edition. She has spoken at PyCon, EuroPython, OSCON, and several regional Python conferences.

" }, - + { - + "serial": 141169, "name": "Matt Ray", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_141169.jpg", @@ -11740,9 +11740,9 @@ "twitter": "mattray", "bio": "

Matt Ray is an open source hacker working as the Director of Cloud Integrations for the company and open source systems integration platform Chef. He is active in the Chef, Ruby and OpenStack communities and was the Community Manager for Zenoss Core. He has been a contributor in the open source community for well over a decade and was one of the founders of the Texas LinuxFest. He resides in Austin, blogs at LeastResistance.net and is @mattray on Twitter, IRC and GitHub.

" }, - + { - + "serial": 77350, "name": "Rob Reilly", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_77350.jpg", @@ -11752,9 +11752,9 @@ "twitter": "", "bio": "

Rob Reilly is an independent consultant, writer, and speaker specializing in Linux, Open Hardware, technology media, and the mobile professional. He\u2019s been hired for a variety of engineering, business analysis, and special projects with AT&T, Intermedia Communications, Lockheed-Martin, Pachube, and Dice. As a 10-year veteran of the tech media, Rob has posted hundreds of feature-length technology articles for LinuxPlanet.com, Linux.com, Linux Journal magazine, PC Update magazine, and Nuts & Volts. He is a co-author of \u201cPoint & Click OpenOffice.org\u201d and worked as a contributing editor for LinuxToday.com. He\u2019s also chaired speaking committees for the old LinuxWorld shows. Rob has a BS in Mechanical Technology from Purdue University and first used the Unix command line in 1981.

" }, - + { - + "serial": 160033, "name": "Ryan Richards", "photo": null, @@ -11764,9 +11764,9 @@ "twitter": null, "bio": "

I am a full stack engineer at Fastly with an interest in the front-end, game programming, and theory. In my spare time I read books, write music, play games, and drink tea.

" }, - + { - + "serial": 11886, "name": "Chris Richardson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_11886.jpg", @@ -11776,9 +11776,9 @@ "twitter": "", "bio": "

Chris Richardson is a developer and architect with over 20 years of experience. He is a Java Champion and the author of POJOs in Action, which describes how to build enterprise Java applications with POJOs and frameworks such as Spring and Hibernate. Chris is the founder of the original CloudFoundry.com, an early Java PaaS (Platform-as-a-Service) for Amazon EC2. He spends his time investigating better ways of developing and deploying software. Chris has a computer science degree from the University of Cambridge in England and lives in Oakland, CA.

" }, - + { - + "serial": 182043, "name": "Bruce Richardson", "photo": null, @@ -11788,9 +11788,9 @@ "twitter": "", "bio": "

Bruce Richardson is the lead software engineer working on the DPDK at Intel Corporation, based out of Shannon in the West of Ireland. He has almost 10 years experience in software for telecoms, and has been working with Intel on the DPDK for over 4 of those. He\u2019s helped design many of the features now present in the DPDK, and when he\u2019s not too busy with other – less interesting \u2013 things at work, occasionally gets to have fun implementing some of them.

" }, - + { - + "serial": 1402, "name": "Bryce Roberts", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_1402.jpg", @@ -11800,9 +11800,9 @@ "twitter": "bryce", "bio": "

Bryce co-founded O’Reilly AlphaTech Ventures (OATV) in 2005. At OATV he focuses on consumer and enterprise software and services investments.

\n

Prior to OATV, Bryce sourced and lead a number of successful early stage investments at Wasatch Venture Fund, a Draper Fisher Jurvetson affiliate. In 2004, Bryce co-founded the Open Source Business Conference (sold to IDG) in order to spark a conversation around commercializing the highly disruptive technologies and services emerging from the open source community. Prior to Wasatch, Bryce was a member of a small team at vertical search pioneer Whizbang! Labs. While at WhizBang!, he defined and launched the FlipDog.com division (sold to Monster Worldwide). He began his career in technology doing large enterprise software deployments, saving his employer from the dreaded Y2K Bug.

\n

His investments at OATV include GameLayers, Get Satisfaction, OpenCandy, OpenX, Parakey (acquired by Facebook), Path Intelligence and Wesabe. He holds a B.A. in Philosophy from Brigham Young University.

" }, - + { - + "serial": 173173, "name": "Gail Roper", "photo": null, @@ -11812,9 +11812,9 @@ "twitter": "gailmroper", "bio": "

28 year IT executive, CIO, and C-level administrator. Vast experience in the area of strategic planning, IT consolidation, and large scale system implementation including ERP, GIS, VoIP, web efforts and fiber/conduit implementation projects. Understands the value of partnerships, well connected with national strategist and innovative thinkers. Fortunate to have well-rounded experience in progressive environments that influence national strategies for innovation. Highly effective in political arenas, governance strategy, and working with auditing process. Has historically brought resources to the organization in the form of grants, funding, and partnerships with public and private entities.

\n

Specialties: Strategic thinker with the idea that technology should promote efficiency in any organization. Focused on innovation and promoting the use of technology to solve problems. Significant success in the area of public/private partnerships, multi-jurisictional efforts, complex contract negotiations, RFP negotiations and evaluations. Prior experience in the development of cost models, Return on Investment strategies, and business case models including financial self funding models. Accountable leader maintaining a focus on productivity and effective practices.

" }, - + { - + "serial": 173247, "name": "Erik Rose", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173247.jpg", @@ -11824,9 +11824,9 @@ "twitter": "ErikRose", "bio": "

Erik Rose leads Mozilla\u2019s DXR project, which does regex searches and static analysis on large codebases like Firefox. He is an Elasticsearch veteran, maintaining the pyelasticsearch library, transitioning Mozilla\u2019s support knowledgebase to ES, and building a burly cluster to do realtime fuzzy matching against the entire corpus of U.S. voters. Erik is a frequent speaker at conferences around the world, the author of \u201cPlone 3 for Education\u201d, and the nexus of the kind of animal magnetism that comes only from writing your own bio.

" }, - + { - + "serial": 124630, "name": "William A Rowe Jr", "photo": null, @@ -11836,9 +11836,9 @@ "twitter": "wrowe", "bio": "

William is a member of the Application Products engineering team at Pivotal, where he has developed and maintained Apache Web Server based products since the turn of the century. He is an active committer to several Apache Software Foundation projects and serves on the ASF security response team. Over the past dozen years, William has contributed to the Apache Software Foundation, initially as a contributor to the Apache HTTP Server and APR projects, served as a Project Chair to both, mentored a number of projects new to the Foundation, participated on the convention committee for the foundation, and served as a Director of the Foundation. He is sometimes teased as the Unix developer who happens to work on Windows, and was largely responsible for stabilizing httpd running on Windows and ensuring Windows was a first class supported platform of the APR library.

" }, - + { - + "serial": 152026, "name": "Byron Ruth", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_152026.jpg", @@ -11848,9 +11848,9 @@ "twitter": "thedevel", "bio": "

Byron Ruth is a Lead Analyst/Programmer in the Center for Biomedical Informatics at The Children’s Hospital of Philadelphia. Byron’s skills in advanced web programming environments, API, and architectural software design have enabled him to lead a variety of projects at CHOP, including the development of a highly integrated audiology research database, an electronic health record-mediated clinical decision support engine for the care of premature infants, and a data management system that helps to discover relationships between genetic markers of congenital heart defects and clinical outcomes.

" }, - + { - + "serial": 172610, "name": "Justin Ryan", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172610.jpg", @@ -11860,9 +11860,9 @@ "twitter": "quidryan", "bio": "

Justin Ryan is a Senior Software Engineer in the Engineering Tools team at Netflix, where he applies his years of experience as a developer to the problems of build automation and dependency management. He’s consistently raising the bar for the quality of build tools and build analysis. He is tasked with finding patterns and best practices between builds and applying them back to the hundreds of projects at Netflix. Justin has worked on Web UIs to Server development to Embedded programming.

" }, - + { - + "serial": 114822, "name": "Baruch Sadogursky", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_114822.jpg", @@ -11872,9 +11872,9 @@ "twitter": "jbaruch", "bio": "

Baruch Sadogursky (a.k.a JBaruch) is the Developer Advocate of JFrog, the creators of Artifactory Binary Repository, the home of Bintray, JavaOne 2011 and 2013 Duke Choice Awards winner.

\n

For a living he hangs out with the JFrog tech leaders, writes some code around Artifactory and Bintray, and then speaks and blogs about all that. He does it repeatedly for the last 10 years and enjoys every moment of it.

\n

Baruch mostly blogs on http://blog.bintray.com and http://blogs.jfrog.org. His speaker history is on Lanyrd

" }, - + { - + "serial": 173464, "name": "Nathan Samano", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173464.jpg", @@ -11884,9 +11884,9 @@ "twitter": "", "bio": "

Nathan is a junior Computer Science major at Saint Joseph’s College. He is minoring in biology. His computing interests include game development and cloud programming.

" }, - + { - + "serial": 169557, "name": "Peter Sand", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_169557.jpg", @@ -11896,9 +11896,9 @@ "twitter": "", "bio": "

Peter has a Ph.D. in Computer Science from MIT. He is the founder of ManyLabs, a nonprofit focused on teaching math and science using sensors and simulations. Peter also founded Modular Science, a company working on hardware and software tools for science labs. He has given talks at Science Hack Day, Launch Edu, and multiple academic conferences, including SIGGRAPH.

" }, - + { - + "serial": 173364, "name": "Karen Sandler", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173364.jpg", @@ -11908,9 +11908,9 @@ "twitter": "o0karen0o", "bio": "

Karen M. Sandler is the Executive Director of the GNOME Foundation. She is known for her advocacy for free software, particularly in relation to the software on medical devices. Prior to joining GNOME, she was General Counsel of the Software Freedom Law Center. Karen continues to do pro bono legal work with SFLC and serves as an officer of the Software Freedom Conservancy and an advisor to the Ada Initiative. She is also pro bono General Counsel to QuestionCopyright.Org. Karen is a recipient of the O’Reilly Open Source Award.

" }, - + { - + "serial": 2699, "name": "Ed Schipul", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_2699.jpg", @@ -11920,9 +11920,9 @@ "twitter": "eschipul", "bio": "

Ed Schipul is CEO of Tendenci, formerly Schipul, a 16-year-old bootstrapped company started in Houston Texas. Ed\u2019s team created the Open Source software platform Tendenci, an all in one CMS built specifically for nonprofits, membership associations and arts organizations.

\n

Under Ed’s leadership, the company has been listed among Houston’s fastest growing companies by the Houston Business Journal, won the Fastech 50, the Aggie 100, and numerous other awards. Ed has personally been nominated for the Ernst and Young Entrepreneur of the year award twice and roasted by the AIGA.

\n

Ed has presented at OSCON, SXSW Interactive, Public Relations Society of America\u2019s International Conference, the Bulldog Reporter National Conference, the Sarasota International Design Summit, Mom 2.0 and dozens of other organizations.

\n

Ed is frequently asked to share his insights regarding online marketing and has been published in Nonprofit World, Association News, The Public Relations Strategist and PR Tactics, among others. He blogs for Hearst Newspaper\u2019s Houston City Brights and has been interviewed on NBC and ABC news as an expert in successfully using digital marketing tools to accelerate organizational growth.

\n

As a past participant and sponsor of the AIR Accessibility Internet Rally community hackathon, Ed has worked to make technology globally accessible, especially for those with disabilities.

\n

Ed is a graduate of Texas A&M University, builds aerial drones as a hobby, is an amateur photographer and a very amateur tennis player.

" }, - + { - + "serial": 128980, "name": "Aaron Schlesinger", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_128980.jpg", @@ -11932,9 +11932,9 @@ "twitter": "arschles", "bio": "

I joined PayPal as part of the StackMob acquisition in late 2013, and I’m currently a Sr. Member of the Technical Staff here. My areas of interest include large scale distributed systems, high throughput server architectures, concurrency in large scale data systems, functional programming patterns, and emerging devops patterns.

\n

In my personal life, I live to play and watch soccer. My goal is to be playing well into my 50s.

" }, - + { - + "serial": 108125, "name": "Nathaniel Schutta", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_108125.jpg", @@ -11944,9 +11944,9 @@ "twitter": "ntschutta", "bio": "

Nathaniel T. Schutta is a solution architect focussed on making usable applications. A proponent of polyglot programming, Nate has written two books on Ajax and speaks regularly at various worldwide conferences, No Fluff Just Stuff symposia, universities, and Java user groups. In addition to his day job, Nate is an adjunct professor at the University of Minnesota where he teaches students to embrace dynamic languages. Most recently, Nate coauthored the book Presentation Patterns with Neal Ford and Matthew McCullough.

" }, - + { - + "serial": 181502, "name": "Daniel Juyung Seo", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_181502.jpg", @@ -11956,9 +11956,9 @@ "twitter": "seojuyung", "bio": "

Daniel Juyung Seo is a software engineer at Samsung Electronics focusing on the development and improvements of EFL. He joined the EFL/Enlightenment community in 2010 and is actively involved the project. He is mostly working on the Ecore core library and Elementary widget set among many libraries in EFL and helps people develop EFL applications on Tizen. As an active technical writer, he contributed EFL and Tizen articles to magazines and manages his own blogs. He participated in Tizen Camera (NX300) and Tizen Wearable Device (Gear 2) product projects in the past as well.

" }, - + { - + "serial": 24052, "name": "Andrew Clay Shafer", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_24052.jpg", @@ -11968,9 +11968,9 @@ "twitter": "littleidea", "bio": "

Andrew has been a consumer and contributor to open source for many years. He is always up for an adventure with a broad background contributing to technology and business efforts. As a co-founder at Puppet Labs, followed by working in and around the CloudStack and OpenStack ecosystems, he gained a lot of perspective on building open source businesses and communities. Sometimes he has been known to talk about devops and organizational learning. Andrew recently joined Pivotal, starting another open source business adventure focused on Cloud Foundry.

" }, - + { - + "serial": 173314, "name": "Brent Shaffer", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173314.jpg", @@ -11980,9 +11980,9 @@ "twitter": "bshaffer", "bio": "

The tale of a young musician with his head filled with hopes and dreams who finds himself burdened with a mathematical mind and with little hopes of making it in the music world, decides to jump ship and finds he loves programming as much and maybe even more than his original passion.

" }, - + { - + "serial": 126882, "name": "Gwen Shapira", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_126882.jpg", @@ -11992,9 +11992,9 @@ "twitter": "gwenshap", "bio": "

Gwen Shapira is a Solutions Architect at Cloudera and leader of IOUG Big Data SIG. Gwen Shapira studied computer science, statistics and operations research at the University of Tel Aviv, and then went on to spend the next 15 years in different technical positions in the IT industry. She specializes in scalable and resilient solutions and helps her customers build high-performance large-scale data architectures using Hadoop. Gwen Shapira is a frequent presenter at conferences and regularly publishes articles in technical magazines and her blog.

" }, - + { - + "serial": 151691, "name": "Roman Shaposhnik", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_151691.jpg", @@ -12004,9 +12004,9 @@ "twitter": "", "bio": "

Roman Shaposhnik is a committer on Apache Hadoop, and holds a Chair position for the Apache Bigtop and Apache Incubator projects. Roman has been involved in Open Source software for more than a decade and has hacked projects ranging from the Linux kernel to the flagship multimedia library known as FFmpeg. He grew up in Sun Microsystems where he had an opportunity to learn from the best software engineers in the industry. Roman’s alma mater is St. Petersburg State University, Russia where he studied to be a mathematician.

" }, - + { - + "serial": 141561, "name": "Michael Shiloh", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_141561.jpg", @@ -12016,9 +12016,9 @@ "twitter": "michaelshiloh", "bio": "

Artist, designer, tinkerer, teacher, geek; practitioner and supporter of Open Source Hardware, Open Source Software, Open Education, Linux, and Arduino, and most things in between

" }, - + { - + "serial": 169647, "name": "Egle Sigler", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_169647.jpg", @@ -12028,9 +12028,9 @@ "twitter": "eglute", "bio": "Egle Sigler is a Private Cloud Architect, who started working at Rackspace in 2008. She was one of the first to receive Rackspace OpenStack certification. Before working with OpenStack, Egle worked on MyRackspace customer control panel, software architecture and enterprise tools. In her spare time, Egle enjoys traveling, hiking, snorkeling and nature photography." }, - + { - + "serial": 3189, "name": "Ricardo Signes", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_3189.jpg", @@ -12040,9 +12040,9 @@ "twitter": "rjbs", "bio": "

Ricardo Signes was thrust into the job market with only a rudimentary humanities education, and was forced to learn to fend for himself. He is now a full-time Perl programmer, the project manager for Perl 5, and frequent contributor to the CPAN.

" }, - + { - + "serial": 180050, "name": "Alan Sill", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_180050.jpg", @@ -12052,9 +12052,9 @@ "twitter": "ogfstandards", "bio": "

Alan Sill directs the National Science Foundation Center for Cloud and Autonomic Computing at Texas Tech University, where he is also a senior scientist at the High Performance Computing Center. A particle physicist by training, he serves as VP of Standards for Open Grid Forum, co-chairs the US National Institute of Standards and Technology “Standards Acceleration to Jumpstart Adoption of Cloud Computing” working group, and co-edits the IEEE Cloud Computing magazine. He is active in many cloud standards working groups and on national and international standards roadmap committees and is committed to development of advanced distributed computing methods for real-world science and business applications.

" }, - + { - + "serial": 74368, "name": "Kyle Simpson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_74368.jpg", @@ -12064,9 +12064,9 @@ "twitter": "getify", "bio": "

Kyle Simpson is an Open Web Evangelist from Austin, TX. He’s passionate about JavaScript, HTML5, real-time/peer-to-peer communications, and web performance. Otherwise, he’s probably bored by it. Kyle is an author, workshop trainer, tech speaker, and avid OSS community member.

" }, - + { - + "serial": 156591, "name": "Samantha Simpson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_156591.jpg", @@ -12076,9 +12076,9 @@ "twitter": "SamHSimpson", "bio": "

Samantha Simpson moved to Washington, D.C. to work at the Consumer Financial Protection Bureau in the Office of Technology and Innovation. She work as both a Technology Portfolio Manager and Product Director. Samantha is passionate about helping consumer understand their finances. Samantha was born and raised on the South Side of Chicago and is a graduate of The Johns Hopkins University.

" }, - + { - + "serial": 165643, "name": "Thomas Smith", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_165643.jpg", @@ -12088,9 +12088,9 @@ "twitter": "projectgado", "bio": "

Thomas Smith is an inventor and entrepreneur, and the creator of the open source Gado 2 archival scanning robot. Hailed by the Wall Street Journal as “a robot which rescues black history”, the Gado 2 has been used to digitize 120,000+ images in the archives of the Afro American Newspapers, and is now in use at archives from California to Finland. Mr. Smith currently leads Project Gado, as well as several other ventures.

" }, - + { - + "serial": 131729, "name": "Garrett Smith", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_131729.jpg", @@ -12100,9 +12100,9 @@ "twitter": "gar1t", "bio": "

Garrett Smith is senior architect at CloudBees, the company behind Jenkins CI and leading Java platform-as-a-service vendor. Garrett specializes in distributed systems and reliable software. His language of choice for systems programming is Erlang, a high productivity functional language specializing in concurrency and reliability. Garrett is an Erlang instructor and the author of the e2 library, which was built from his experience teaching Erlang. Garrett is also known for the videos “MongoDB is Web Scale” and “Node.js Is Bad Ass Rock Star Tech”.

" }, - + { - + "serial": 109116, "name": "Chris Smith", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_109116.jpg", @@ -12112,9 +12112,9 @@ "twitter": "chrissmithus", "bio": "

Chris Smith is a web site architect for Xerox.com by day and transportation geek and City of Portland Planning and Sustainability Commissioner by night

" }, - + { - + "serial": 77757, "name": "Bryan Smith", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_77757.jpg", @@ -12124,9 +12124,9 @@ "twitter": "fossetcon", "bio": "

Bryan A Smith is a Debian Gnu/Linux and BSD enthusiast, hardware hacker and Systems Engineer. Bryan has used Open Source Operating Systems since the days of Red Hat 5 Hurricane.

\n

He contributes to several Open Source projects and has helped launch several startup ISP’s based in his area using Free and Open Source software as a framework.

\n

Bryan is currently the Program Director for Fossetcon Fossetcon – Free and Open Source Software Expo and Technology Conference

\n

Bryan spends his free time organizing Fossetcon, administering his free shell server, advocating Free and Open Source, proctoring BSD Associate Certification, writing poetry and playing Bossa Nova and Flamenco guitar.

" }, - + { - + "serial": 81759, "name": "Carol Smith", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_81759.jpg", @@ -12136,9 +12136,9 @@ "twitter": "fossygrl", "bio": "

Carol Smith is the Open Source Programs Manager running Google
\nSummer of Code. She’s been at Google as a program manager for 9 years.
\nShe has a degree in photojournalism from California State University,
\nNorthridge, and is an avid cyclist and bibliophile.

" }, - + { - + "serial": 2732, "name": "Nathan Sobo", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_2732.jpg", @@ -12148,9 +12148,9 @@ "twitter": "nathansobo", "bio": "

Nathan Sobo is the co-founder of the Atom open-source text editor at GitHub. He is also the author of Treetop, a Ruby-based parser generator based on parsing expression grammars.

" }, - + { - + "serial": 44753, "name": "Juhan Sonin", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_44753.jpg", @@ -12160,9 +12160,9 @@ "twitter": "", "bio": "

Juhan Sonin is the Creative Director of Involution Studios and has produced work recognized by the New York Times, Newsweek, BBC International, Billboard Magazine, and National Public Radio (NPR). He has spent time at Apple, the National Center for Supercomputing Applications (NCSA), and MITRE. Juhan lectures on design and engineering at the Massachusetts Institute of Technology (MIT).

" }, - + { - + "serial": 178738, "name": "Andrew Sorensen", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_178738.jpg", @@ -12172,9 +12172,9 @@ "twitter": "digego", "bio": "

Andrew Sorensen is an artist-programmer whose interests lie at the
\nintersection of computer science and creative practice. Andrew is well
\nknown for creating the programming languages that he uses in live
\nperformance to generate improvised audiovisual theatre. He has been
\ninvited to perform these contemporary audiovisual improvisations
\naround the world. Andrew is a Senior Research Fellow at the Queensland
\nUniversity of Technology and is the author of the Impromptu and
\nExtempore programming language environments.

" }, - + { - + "serial": 175353, "name": "Derek Sorkin", "photo": null, @@ -12184,9 +12184,9 @@ "twitter": "", "bio": "

GitHub

" }, - + { - + "serial": 151991, "name": "Francisco Souza", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_151991.jpg", @@ -12196,9 +12196,9 @@ "twitter": "franciscosouza", "bio": "

Francisco Souza is a software engineer at Globo.com, the largest media company in Latin America. He works in the cloud platform team, building an open source solution to be adopted by all Globo.com portals.

" }, - + { - + "serial": 142767, "name": "Kara Sowles", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_142767.jpg", @@ -12208,9 +12208,9 @@ "twitter": "FeyNudibranch", "bio": "

Kara Sowles is Community Initiatives Manager at Puppet Labs in Portland, OR. She’s excited to swap user group tips with you for far too long.

\n

When she’s done planning events and running community programs at Puppet Labs, she enjoys going home and making stop motion animation with actual Puppets.

" }, - + { - + "serial": 172240, "name": "Kaushik Srenevasan", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172240.jpg", @@ -12220,9 +12220,9 @@ "twitter": "", "bio": "

Kaushik Srenevasan is the Lead on the VM Diagnostics team at Twitter,
\nwhere he hacks on the Hotspot JVM, on, among other things, improving
\nits observability. Before joining Twitter he authored the Chakra
\nJavaScript runtime’s AMD64 backend compiler and worked on the .NET CLR at
\nMicrosoft.

" }, - + { - + "serial": 131499, "name": "Raghavan Srinivas", "photo": null, @@ -12232,9 +12232,9 @@ "twitter": "ragss", "bio": "

Raghavan “Rags” Srinivas works as a Cloud Solutions Architect. His general focus area is in distributed systems, with a specialization in Cloud Computing and Big Data. He worked on Hadoop, HBase and NoSQL during its early stages. He has spoken on a variety of technical topics at conferences around the world, conducted and organized Hands-on Labs and taught graduate classes in the evening.

\n

Rags brings with him over 25 years of hands-on software development and over 15 years of architecture and technology evangelism experience.

\n

Rags holds a Masters degree in Computer Science from the Center of Advanced Computer Studies at the University of Louisiana at Lafayette. He likes to hike, run and generally be outdoors but most of all loves to eat.

" }, - + { - + "serial": 3476, "name": "Simon St. Laurent", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_3476.jpg", @@ -12244,9 +12244,9 @@ "twitter": "simonstl", "bio": "

Simon St.Laurent has spent most of his career explaining technology. He is co-chair of the Fluent and OSCON conferences, a Senior Editor at O’Reilly, and a web developer. He has written over a dozen books, including Introducing Elixir, Introducing Erlang, Learning Rails, XML Elements of Style, and XML: A Primer. He spends his spare time making objects out of wood and presenting on local history.

" }, - + { - + "serial": 170134, "name": "Mark Stanislav", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_170134.jpg", @@ -12256,9 +12256,9 @@ "twitter": "markstanislav", "bio": "

Mark Stanislav is the Security Evangelist for Duo Security, an Ann Arbor, Michigan-based startup focused on two-factor authentication and mobile security. With a career spanning over a decade, Mark has worked within small business, academia, startup, and corporate environments, primarily focused on Linux architecture, information security, and web application development.

\n

Mark has spoken nationally at over 70 events including RSA, ISSA, B-Sides, GrrCon, Infragard, and the Rochester Security Summit. Mark\u2019s security research has been featured on web sites including CSO Online, Security Ledger, and Slashdot. Additionally, Mark is an active participant of local and nationals security organizations including ISSA, Infragard, HTCIA, ArbSec, and MiSec.

\n

Mark earned his Bachelor of Science Degree in Networking and IT Administration and his Master of Science Degree in Technology Studies, focused on Information Assurance, both from Eastern Michigan University. During his time at EMU, Mark built the curriculum for two courses focused on Linux administration and taught as an Adjunct Lecturer for two years. Mark holds CISSP, Security+, Linux+, and CCSK certifications.

" }, - + { - + "serial": 172899, "name": "Nicolas Steenhout", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172899.jpg", @@ -12268,9 +12268,9 @@ "twitter": "vavroom", "bio": "

Nicolas Steenhout is a veteran of, and passionate advocate for, web accessibility. Nic had taken the lead in building several websites prior to taking up a federally-funded position in the disability sector in the US in 1996. An international speaker, trainer and consultant, Nic works with government, corporations, and small teams, in the areas of both web and physical accessibility. Working with and for thousands of people with disabilities in North America and Australasia, while working with web technologies and their impact, has given Nic a unique insight into the challenges, solutions, nuts and bolts of web accessibility.

" }, - + { - + "serial": 138530, "name": "Boyd Stephens", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_138530.jpg", @@ -12280,9 +12280,9 @@ "twitter": "", "bio": "

Boyd Stephens – the founder of Netelysis, LLC and is currently employed within the firm’s network engineering division. Having entered his 17th year as an entrepreneur, Boyd is now a member of Netelysis’ networking services development team where his primary focus is upon the research and design of internetworking systems and network management services.

\n

For the last twenty-six years he has worked within some facet of the information technology industry having designed and managed an array of networking systems and services for educational, corporate and governmental entities. His professional efforts have been the source of immeasurable fulfillment both personally and professionally.

" }, - + { - + "serial": 172656, "name": "Simon Stewart", "photo": null, @@ -12292,9 +12292,9 @@ "twitter": "shs96c", "bio": "

Simon works at Facebook as a Software Engineer, where he works as part of the internal tools team. He’s also the inventor of WebDriver and the current lead of the Selenium project, and co-edits the W3C WebDriver specification.

" }, - + { - + "serial": 173088, "name": "Matt Stine", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173088.jpg", @@ -12304,9 +12304,9 @@ "twitter": "mstine", "bio": "

Matt Stine is a Cloud Foundry Platform Engineer at Pivotal. He is a 13-year veteran of the enterprise IT industry, with experience spanning numerous business domains.

\n

Matt is obsessed with the idea that enterprise IT \u201cdoesn\u2019t have to suck,\u201d and spends much of his time thinking about lean/agile software development methodologies, DevOps, architectural principles/patterns/practices, and programming paradigms, in an attempt to find the perfect storm of techniques that will allow corporate IT departments to not only function like startup companies, but also create software that delights users while maintaining a high degree of conceptual integrity. He currently specializes in helping customers achieve success with Platform as a Service (PaaS) using Cloud Foundry and BOSH.

\n

Matt has spoken at conferences ranging from JavaOne to CodeMash, is a regular speaker on the No Fluff Just Stuff tour, and serves as Technical Editor of NFJS the Magazine. Matt is also the founder and past president of the Memphis/Mid-South Java User Group.

" }, - + { - + "serial": 122293, "name": "Deirdr\u00e9 Straughan", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_122293.jpg", @@ -12316,9 +12316,9 @@ "twitter": "deirdres", "bio": "

In a career spanning tech companies large and small, worldwide, Deirdr\u00e9 Straughan has constantly developed new ways to foster communication about technology between customers and companies, online and offline. She has particularly focused on innovative uses of media, in recent years producing hundreds of technical videos and live video streams for Sun Microsystems, Oracle, and Joyent, and for open source communities including OpenSolaris, illumos, SmartOS, and OpenZFS. You can learn more on beginningwithi.com.

" }, - + { - + "serial": 135908, "name": "Jason Strimpel", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_135908.jpg", @@ -12328,9 +12328,9 @@ "twitter": "", "bio": "

I engineer software.

" }, - + { - + "serial": 108840, "name": "Ruth Suehle", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_108840.jpg", @@ -12340,9 +12340,9 @@ "twitter": "suehle", "bio": "

Ruth Suehle is a community marketing manager in Red Hat\u2019s Open Source and Standards group, which supports upstream open source software communities. She also leads the Fedora Project\u2019s marketing team and is co-author of Raspberry Pi Hacks (O\u2019Reilly, December 2013). Previously an editor for Red Hat Magazine, she now leads discussions about open source principles as an editor at opensource.com. Ruth is also a senior editor at GeekMom.com, where she covers the adventures of motherhood alongside technology and sci-fi.

" }, - + { - + "serial": 173421, "name": "Marc Sugiyama", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173421.jpg", @@ -12352,9 +12352,9 @@ "twitter": "", "bio": "

Marc Sugiyama is a Senior Architect at Erlang Solutions, Inc. A Bay Area native, he has 30 years of software development experience and has worked on everything from testing frameworks in Tcl at Sybase and Cisco, to SMP relational database engines in C at Sybase, to a MMO engine in Twisted Python for Pixverse (a company he co-founded), to a large scale real time chat system in Erlang for hi5 Networks. Prior to joining Erlang Solutions, he built a call handling service in Erlang for Ribbit/British Telecom leading a team of developers in Brazil, Sweden, the US, and the UK. A published author, he wrote his first magazine articles and books while still in high school. He has presented at Sybase User Group Meetings and the Colorado Software Summit. He holds a Bachelors of Science in Engineering and Masters of Engineering from Harvey Mudd College (Claremont, CA) and serves on the Board of Trustees of The College Preparatory School in Oakland, CA.

" }, - + { - + "serial": 164229, "name": "Nick Sullivan", "photo": null, @@ -12364,9 +12364,9 @@ "twitter": "grittygrease", "bio": "

Nick is a software engineering leader innovating in the world of Internet scale data at CloudFlare. He is also a respected digital rights management innovator with a thorough understanding of the digital media distribution process through over half a decade working on the iTunes store. He previously worked as a security analyst worked at Symantec analyzing large scale threat data. He holds an MSc in Cryptography and a BMath in Pure Mathematics.

" }, - + { - + "serial": 175040, "name": "Zach Supalla", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_175040.jpg", @@ -12376,9 +12376,9 @@ "twitter": "zsupalla", "bio": "

Zach Supalla is an entrepreneur, a Maker, and a designer. He is the founder and CEO of Spark, a start-up that’s making it easier to build internet-connected hardware. Zach juggles hardware design, front-end software development, and leading his team through the trials and tribulations of a hardware start-up.

\n

The Spark team led a successful Kickstarter campaign for their product, the Spark Core, in May 2013, raising nearly $600K in 30 days off of a goal of $10K. They\u2019re now shipping to 61 countries, with thousands of engineers and developers building new connected devices with their technology. Their products have been featured in WIRED, Engadget, Fast Company, TechCrunch, the Discovery Channel, and many other publications.

\n

Zach is a graduate of HAXLR8R, the only incubator for hardware start-ups that will teach you to order bubble tea in perfect Mandarin. He also has an MBA from Kellogg School of Management and an MEM (Masters in Engineering Management) from McCormick School of Engineering at Northwestern. Before Spark, Zach worked as a management consultant with McKinsey & Company, advising Fortune 500 companies on strategy, operations, and product development.

" }, - + { - + "serial": 171226, "name": "Jason Swartz", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171226.jpg", @@ -12388,9 +12388,9 @@ "twitter": "swartzrock", "bio": "

Jason is a Software Engineer in the San Francisco Bay Area, developing applications and services in Scala at Netflix. Before making the switch to functional programming he managed the developer docs and support team at eBay, wrote advertising and merchandising platforms in Java and built tools and UI prototypes at Apple. His book, “Learning Scala”, will be published by O’Reilly Media in Spring 2014.

" }, - + { - + "serial": 153857, "name": "Kiyoto Tamura", "photo": null, @@ -12400,9 +12400,9 @@ "twitter": null, "bio": "

Kiyoto is one of the maintainers of Fluentd, the open source log collector with users ranging from Nintendo to Slideshare. Raised in Japan, New York and California, he brings a unique bilingual, bicultural perspective to the open source world.

\n

He spends much of his day at Treasure Data as a developer marketer/community manager for Fluentd. He also is a math nerd turned quantitative trader turned software engineer turned open source community advocate and cherishes American brunch and Japanese game shows.

" }, - + { - + "serial": 173262, "name": "Paul Tarjan", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173262.jpg", @@ -12412,9 +12412,9 @@ "twitter": "ptarjan", "bio": "

I’m a juggling unicylcing web hacker @ Facebook.

\n

I’m on the HipHop for PHP team making HHVM able to run all existing PHP code. Before that, I build many of the Open Graph tools and features. Before that I was the Tech Lead for Yahoo! SearchMonkey.

" }, - + { - + "serial": 41059, "name": "James Tauber", "photo": null, @@ -12424,9 +12424,9 @@ "twitter": "jtauber", "bio": "

James Tauber is an Australian web developer and entrepreneur now based in Boston. He has been involved in open source, Web standards and Python for over 15 years.

" }, - + { - + "serial": 173285, "name": "Christian Ternus", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173285.jpg", @@ -12436,9 +12436,9 @@ "twitter": "ternus", "bio": "

Christian Ternus is a security researcher on Akamai Technologies’ Adversarial Resilience team, where he works on attacks, architecture, design, analysis, and the human factors in security. He graduated from MIT and has previously worked in kernel security and mobile health-tech. He has previously spoken at industry conferences including Boston Security, SOURCE Boston, and BrainTank, as well as organizing Akamai’s Humanity in Security miniconference. In his spare time, he is an avid photographer and adventure motorcyclist.

" }, - + { - + "serial": 173472, "name": "Jeffrey Thalhammer", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173472.jpg", @@ -12448,9 +12448,9 @@ "twitter": "thaljef", "bio": "

Jeffrey Thalhammer has been developing software for more than 15 years. He is the creator of Perl::Critic, the widely used static analyzer for Perl. Jeffrey is also a co-organizer of the San Francisco Perl Mongers.

" }, - + { - + "serial": 150, "name": "Laura Thomson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_150.jpg", @@ -12460,9 +12460,9 @@ "twitter": null, "bio": "

Laura Thomson is a Senior Engineering Manager at Mozilla Corporation. She works with the Web Engineering team, which is responsible for the Firefox crash reporting system and other developer tools, and the Release Engineering team, which is responsible for shipping Firefox.

\n

Laura is the co-author of \u201cPHP and MySQL Web Development\u201d and \u201cMySQL Tutorial\u201d. She is a veteran speaker at Open Source conferences worldwide.

" }, - + { - + "serial": 173378, "name": "Sebastian Tiedtke", "photo": null, @@ -12472,9 +12472,9 @@ "twitter": "sourishkrout", "bio": "

Born and raised at the foot of the Alps just outside of Munich in Germany, Sebastian spent his youth listening to David Hasselhoff songs. He wore Lederhosen and eagerly anticipated the day he could legally drink his first stein of beer.

\n

When daddy put the first computer on his desk in the mid 90s, Sebastian quickly went on to figure out BASIC, Pascal and C/C++. Rainy afternoons were filled with building websites, countless iterations of Linux kernel makefile tweaks and compiler runs.

\n

Before joining Sauce, Sebastian finished a degree in Software Development (in Munich), he spent almost a decade building customer information systems making transit information more accessible for people living in large metropolitan areas worldwide. His job commitment and passions for technology eventually made him move to San Francisco in 2009.

\n

When Sebastian is not busy improving the Sauce experience he likes to take joyrides on his 1961 vespa. He’s a natural optimist in life and always sees the stein half full.

" }, - + { - + "serial": 53442, "name": "Jim Tommaney", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_53442.jpg", @@ -12484,9 +12484,9 @@ "twitter": "InfiniDB", "bio": "

Jim has extensive experience in leading the development, management, and performance for enterprise data architectures, including clustered, large SMP, and distributed systems for the retail, web, and telecom industries. He is responsible for the architecture, vision, direction, and technical evangelization of InfiniDB. Jim holds a BBA from Texas A&M and a Masters in Management Information Systems from the University of Texas at Dallas.

" }, - + { - + "serial": 172661, "name": "Sudhir Tonse", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172661.jpg", @@ -12496,9 +12496,9 @@ "twitter": "stonse", "bio": "

Sudhir Tonse manages the Cloud Platform Infrastructure team at Netflix and is responsible for many of the services and components that form the Netflix Cloud Platform as a Service.

\n

Many of these components have been open sourced under the NetflixOSS umbrella. Open source contribution includes Archaius: a dynamic configuration/properties management library, Ribbon: a Inter Process Communications framework that includes Cloud friendly Software load balancers, Karyon: the nucleus of a PaaS service etc.
\nPrior to Netflix, Sudhir was an Architect at Netscape/AOL delivering large-scale consumer and enterprise applications in the area of Personalization, Infrastructure and Advertising Solutions.

\n

Sudhir is a weekend golfer and tries to make the most of the wonderful California weather and public courses.

" }, - + { - + "serial": 173326, "name": "Willem Toorop", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173326.jpg", @@ -12508,9 +12508,9 @@ "twitter": "", "bio": "

Willem is a developer at NLnet Labs, a not-for-profit foundation dedicated to the development of open-source implementations of open standards. At NLnet Labs Willem is the lead developer of the C DNS utility library: ldns. Willem has implemented leading edge DNS functionality for ldns based on new open standards such as DNSSEC and DANE. Our getdns-api implementation utilizes ldns for processing DNS data. Another of NLnet Labs C-libraries, libunbound, is used for DNS resolving. Besides working on ldns Willem also maintains and develops the perl Net::DNS and Net::DNS::SEC modules and actively researches Path MTU black holes that hamper DNSSEC deployment.

" }, - + { - + "serial": 181972, "name": "Joan Touzet", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_181972.jpg", @@ -12520,9 +12520,9 @@ "twitter": "wohali", "bio": "

Joan Touzet has been using Apache CouchDB since 2008. She is now a committer and PMC member for Apache CouchDB, and acts as Senior Software Development Manager at Cloudant, an IBM Company.

\n

Her major effort for 2014 is coordinating the merge of Cloudant’s BigCouch branch back into CouchDB towards a CouchDB 2.0 release. Recently, Joan has given talks at CloudantCON 2014, CouchDB Conf 2013, ChefConf 2013 and PyCon Canada 2012, all of which are viewable on YouTube.

\n

In her spare time, Joan builds and repairs motorcycles, pilots small aircraft, and composes music for video game soundtracks.

" }, - + { - + "serial": 172990, "name": "Brian Troutwine", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172990.jpg", @@ -12532,9 +12532,9 @@ "twitter": "bltroutwine", "bio": "

I’m a software engineer at AdRoll where I work on soft real-time system in Erlang. I deal with mission-critical systems that operate at large scale. Functional programming and Erlang in particular are my main areas of interest and I’m becoming increasingly interested in “large” embedded systems.

\n

I graduated in 2010 with a degree in Computer Science from Portland State University. I spoke at Erlang Factory 2014, Bay Area.

" }, - + { - + "serial": 172607, "name": "Eric Tschetter", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_172607.jpg", @@ -12544,9 +12544,9 @@ "twitter": "zedruid", "bio": "

Eric Tschetter is the creator and one of the main Contributors to Druid, an open source, real-time analytical data store. He is currently an individual contributor to Tidepool.org, a non-profit diabetes research organization. Eric was previously the VP of Engineering and lead architect at Metamarkets, and has held senior engineering positions at Ning and LinkedIn.He holds bachelors degrees in Computer Science and Japanese from the University of Texas at Austin, and a M.S. from the University of Tokyo in Computer Science.

" }, - + { - + "serial": 5060, "name": "James Turnbull", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_5060.jpg", @@ -12556,9 +12556,9 @@ "twitter": "kartar", "bio": "

James Turnbull is the author of seven technical books about open source software and a long-time member of the open source community. James authored the The Logstash Book and The Docker Book. He also wrote two books about Puppet (Pro Puppet\"\" and the earlier book about Puppet as well as Pro Linux System Administration, Pro Nagios 2.0, and Hardening Linux.

\n

For a real job, James is VP of Services for Docker. He likes food, wine, books, photography and cats. He is not overly keen on long walks on the beach and holding hands.

" }, - + { - + "serial": 169870, "name": "James Turner", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_169870.jpg", @@ -12568,9 +12568,9 @@ "twitter": "blackbearnh", "bio": "

James Turner, contributing editor for oreilly.com, is a freelance journalist who has written for publications as diverse as the Christian Science Monitor, Processor, Linuxworld Magazine, Developer.com and WIRED Magazine. In addition to his shorter writing, he has also written two books on Java Web Development (MySQL & JSP Web Applications" and “Struts: Kick Start”) as well as the O’Reilly title “Developing Enterprise iOS Applications”. He is the former Senior Editor of LinuxWorld Magazine and Senior Contributing Editor for Linux Today. He has also spent more than 30 years as a software engineer and system administrator, and currently works as a Senior Software Engineer for a company in the Boston area. His past employers have included the MIT Artificial Intelligence Laboratory, Xerox AI Systems, Solbourne Computer, Interleaf, the Christian Science Monitor and contracting positions at BBN and Fidelity Investments. He is a committer on the Apache Jakarta Struts project and served as the Struts 1.1B3 release manager. He lives in a 200 year old Colonial farmhouse in Derry, NH along with his wife and son. He is an open water diver and instrument-rated private pilot, as well as an avid science fiction fan.

" }, - + { - + "serial": 173406, "name": "Andrew Turner", "photo": null, @@ -12580,9 +12580,9 @@ "twitter": "ajturner", "bio": "

CTO of the Esri DC Dev Center

" }, - + { - + "serial": 175354, "name": "Tim Tyler", "photo": null, @@ -12592,9 +12592,9 @@ "twitter": "", "bio": "

Sr. Staff Engineer
\nQualcomm Open Source Portal Team

" }, - + { - + "serial": 86111, "name": "David Uhlman", "photo": null, @@ -12604,9 +12604,9 @@ "twitter": "", "bio": "

David Uhlman is CEO of ClearHealth Inc. and a has been longstanding contributor and entrepreneur in open source technology for 15 years including contributions to Linux, Java, CentOS, and Joomla. He is a frequent speaker and contributor including previous talks at OSCON, SCALE, and others.

\n

ClearHealth Inc is the company associated with the ClearHealth open source project, a practice management and electronic medical record system derived from VA VistA started in 2003 which now has over 18 million patients in more than 1,000 installations including notable large scale systems like Tarrant County TX, The Primary Care Coalition, The University of Texas Medical Branch and numerous Federally Qualified Health Centers (FQHC) nationwide.

" }, - + { - + "serial": 173056, "name": "Manish Vachharajani", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173056.jpg", @@ -12616,9 +12616,9 @@ "twitter": "mvachhar", "bio": "

Manish was the Founder and Chief Software Architect of LineRate Systems, a high-performance software networking startup that was acquired by F5 Networks in February 2013.

\n

LineRate Systems’ core technology is based on Node.js and Manish’s research group’s work on high-performance networking at the University of Colorado at Boulder.

\n

Prior to LineRate, Manish dedicated 13 years studying
\nsoftware performance on general purpose processors. He co-authored nearly 50 publications on processor performance in a range of fields including optimizing compiler design, on-chip optics, performance modeling, parallel programming, and high performance networking. His work has been recognized by best paper and presentation awards at
\ntop-tier conferences, support from the National Science Foundation, and support from industry leaders such as Intel and NVIDIA. Manish is currently a Senior Architect at F5 Networks leading the design of the
\nLineRate product.

" }, - + { - + "serial": 76735, "name": "Jos\u00e9 Valim", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_76735.jpg", @@ -12628,9 +12628,9 @@ "twitter": "josevalim", "bio": "

Jos\u00e9 Valim is the creator of the Elixir programming language and member of the Rails Core Team. He graduated in Engineering in S\u00e3o Paulo University, Brazil and has a Master of Science by Politecnico di Torino, Italy. He is also the lead-developer of Plataformatec, a consultancy firm based in Brazil, and an active member of the Open Source community.

" }, - + { - + "serial": 99817, "name": "William Van Hevelingen", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_99817.jpg", @@ -12640,9 +12640,9 @@ "twitter": "pdx_blkperl", "bio": "

William Van Hevelingen started with Linux and configuration management as part of the Computer Action Team\u2019s Braindump program at Portland State University. He worked on the Wintel, Nix, and Networking teams as a volunteer and later as a student worker helping to manage hundreds of workstations, servers, and networking infrastructure. William now works full time for the Computer Action Team (TheCAT), which provides IT for the Maseeh College of Engineering and Computer Science at Portland State University, as the Unix Team lead. He helps teach the Unix portion of the CAT\u2019s Braindump program, covering topics like web servers, databases, storage, virtualization, and Puppet. William is a co-author of Pro Puppet 2nd Edition and Beginning Puppet book which is to be released in late 2014.

" }, - + { - + "serial": 65329, "name": "Heather VanCura", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_65329.jpg", @@ -12652,9 +12652,9 @@ "twitter": "", "bio": "

Heather VanCura manages the JCP Program Office and is responsible for the day-to-day nurturing, support, and leadership of the community. She oversees the JCP.org web site, JSR management and posting, community building, events, marketing, communications, and growth of the membership through new members and renewals. Heather has a front row seat for studying trends within the community and recommending changes. Several changes to the program in recent years have included enabling broader participation, increased transparency and agility in JSR development.

" }, - + { - + "serial": 183835, "name": "Poornima Venkatakrishnan", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_183835.jpg", @@ -12664,9 +12664,9 @@ "twitter": null, "bio": "

I am a web developer who is passionate about all things node.js and open source. I am an active contributor/ maintainer of Krakenjs – Paypal’s open sourced app framework for express. I like playing with new technologies, solving difficult problems and working on game changing projects. To sum up, I love a good challenge that keeps me on my toes.

" }, - + { - + "serial": 112672, "name": "Alvaro Videla", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_112672.jpg", @@ -12676,9 +12676,9 @@ "twitter": "old_sound", "bio": "

Alvaro Videla works as Developer Advocate for RabbitMQ/Pivotal. Before moving to Europe he used to work in Shanghai where he helped building one of Germany biggest dating websites. He co-authored the book “RabbitMQ in Action” for Manning Publishing. Some of his open source projects can be found here. Apart from code related stuff he likes traveling with his wife, listening/playing music and reading books.

" }, - + { - + "serial": 182587, "name": "Ryan Vinyard", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_182587.jpg", @@ -12688,9 +12688,9 @@ "twitter": "", "bio": "

Ryan is the Engineering Lead at Highway1, a hardware-focused startup accelerator located in San Francisco under parent company PCH International. He is a Mechanical Engineer who came to PCH through its consulting arm Lime Lab, where he developed consumer products for Fortune 500 brands. Previous to PCH, Ryan worked at startups in the cleantech and electric vehicle space where he developed novel powertrain, motor control, and thermal systems. Ryan holds a B.S. in Product Design from Stanford University.

" }, - + { - + "serial": 174073, "name": "Robert Virding", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_174073.jpg", @@ -12700,9 +12700,9 @@ "twitter": "rvirding", "bio": "

Robert is Principal Language Expert at Erlang Solutions Ltd. He was one of the early members of the Ericsson Computer Science Lab, and co-inventor of the Erlang language. He took part in the original system design and contributed much of the original libraries, as well as to the current compiler. While at the lab he also did a lot of work on the implementation of logic and functional languages, as well as garbage collection.

\n

Robert’s passion is language implementation and he created a Lisp Flavored Erlang (LFE) and Luerl, leveraging his intimate knowledge of the Erlang environment, compiler and VM. He did reactive programming long before it became a buzz word. He is also rumored to be the best Erlang teacher on this planet.

\n

Robert has worked as an entrepreneur and was one of the co-founders of one of the first Erlang startups (Bluetail). He worked a number of years at the Swedish Defence Materiel Administration (FMV) Modelling and Simulations Group. And he co-authored the first book (Prentice-Hall) on Erlang, and is regularly invited to teach and present throughout the world.

" }, - + { - + "serial": 46737, "name": "Karsten Wade", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_46737.jpg", @@ -12712,9 +12712,9 @@ "twitter": null, "bio": "

Since 2000 Karsten has been teaching and living the open source way. As a member of Red Hat’s Open Source and Standards team, he helps with community activities in projects Red Hat is involved in. As a 19 year IT industry veteran, Karsten has worked most sides of common business equations as an IS manager, professional services consultant, technical writer, and developer advocate. As of 2013, Karsten has been working on the CentOS Project as a new Board member, Red Hat liaison on the Board, and engineering team manager. You’ll see him getting involved in infrastructure, documentation, and distro building. He blogs here, microblogs here , and is found on IRC as ‘quaid’.

\n

Karsten lives in his hometown of Santa Cruz, CA with his wife and two daughters on their small urban farm, Fairy-Tale Farm, where they focus on growing their own food and nurturing sustainable community living. Most recently, Karsten has been a partner in a collectively-run business of people-powered transportation, Santa Cruz Pedicab, and some weekends you’ll find him taking tourists and late-nighters around downtown.

" }, - + { - + "serial": 39928, "name": "Ken Walker", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_39928.jpg", @@ -12724,9 +12724,9 @@ "twitter": null, "bio": "

Ken is the lead for the Open Source Orion project. He aims to make web based development tools match and exceed the capabilities of a desktop IDE and not just for Web applications. His work in developer tools includes a long history from ENVY/Smalltalk, VisualAge for Java, VisualAge Micro Edition, Eclipse and now the JavaScript based client side Orion platform. Previously he was responsible for IBM’s J9 Mobile JVM platform.

" }, - + { - + "serial": 1158, "name": "James Ward", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_1158.jpg", @@ -12736,9 +12736,9 @@ "twitter": "_JamesWard", "bio": "

James Ward works for Typesafe where he teaches developers the Typesafe Platform (Play Framework, Scala, and Akka).

" }, - + { - + "serial": 6219, "name": "Simon Wardley", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_6219.jpg", @@ -12748,9 +12748,9 @@ "twitter": "swardley", "bio": "

based in the UK, works for CSC\u2019s Leading Edge Forum, a global research and advisory programme. Simon\u2019s focus is on the intersection of strategy, economics and new technologies.

\n

As a geneticist with a love of mathematics and a fascination in economics, Simon has always found himself dealing with complex systems, whether it\u2019s in behavioural patterns, environmental risks of chemical pollution, developing novel computer systems or managing companies. He is a passionate advocate and researcher in the fields of open source, commoditization, innovation, organizational structure and cybernetics.

\n

Simon is a regular presenter at conferences worldwide, and was voted as one of the UK’s top 50 most influential people in IT in ComputerWeekly’s poll in 2011 and 2012.

" }, - + { - + "serial": 173025, "name": "Corinne Warnshuis", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173025.jpg", @@ -12760,9 +12760,9 @@ "twitter": "corinnepw", "bio": "

Corinne is the Executive Director of Girl Develop It, a nonprofit with chapters in 36 cities that exists to offer affordable classes for women to learn web and software development. She moved to Philadelphia in 2011 after graduating from University of California, Santa Cruz.

" }, - + { - + "serial": 171565, "name": "Phil Webb", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171565.jpg", @@ -12772,9 +12772,9 @@ "twitter": "phillip_webb", "bio": "

Phil Webb is a Spring Framework developer and co-creator of the Spring Boot project. Prior to joining Pivotal and relocating to California, Phil worked for a number of UK technology companies.

" }, - + { - + "serial": 4146, "name": "Emma Jane Westby", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_4146.jpg", @@ -12784,9 +12784,9 @@ "twitter": "emmajanehw", "bio": "

\ufeffEmma Jane Westby (n\u00e9e Hogbin) has been working as a web developer since 1996, and has been participating in FOSS communities for over a decade. She’s authored two books on Drupal (including the ever-popular Front End Drupal), and contributed technical reviews, and articles to many more publications. Passionate about information, and knowledge acquisition, Emma Jane teaches Web-based technologies online, at her local community college, and at conferences around the world. She is well-known in the Drupal community for her Drupal socks and their GPLed pattern. In her spare time, Emma Jane crafts, keeps bees, and likes to drink Scotch. You can find her at emmajane.net.

" }, - + { - + "serial": 141524, "name": "Langdon White", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_141524.jpg", @@ -12796,9 +12796,9 @@ "twitter": "langdonwhite", "bio": "

Evangelist for the Red Hat Enterprise Linux platform and it\u2019s associated Developer Program. Has spent 15 years architecting and implementing high-impact software systems for companies ranging from startups to large companies.

" }, - + { - + "serial": 142111, "name": "Sarah White", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_142111.jpg", @@ -12808,9 +12808,9 @@ "twitter": "carbonfray", "bio": "

Sarah is the co-founder of OpenDevise. She’s passionate about helping open source projects find practical yet fun ways to communicate with their users and contributors.

\n

Long ago, in a not-too-distant galaxy, she assessed hazardous waste sites and tracked pesticide routes through watersheds. So she knows a thing or two about identifying and eradicating stuff that kills projects, including poor documentation.

" }, - + { - + "serial": 54107, "name": "Dustin Whittle", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_54107.jpg", @@ -12820,9 +12820,9 @@ "twitter": "dustinwhittle", "bio": "

Dustin Whittle is a Developer Evangelist at AppDynamics focused on helping organizations manage their application performance. Before joining AppDynamics, Dustin was CTO at Kwarter, a consultant at SensioLabs, and developer evangelist at Yahoo!. He has experience building and leading engineering teams and working with developers and partners to scale up. When Dustin isn’t working he enjoys flying, sailing, diving, golfing, and travelling around the world. Find out more at dustinwhittle.com.

" }, - + { - + "serial": 172895, "name": "Glen Wiley", "photo": null, @@ -12832,9 +12832,9 @@ "twitter": "glenswiley", "bio": "

Glen spent seven years serving as the systems architect for the DNS resolution platforms for the largest domain in the world (.COM and two of the Internet root servers). He currently works in an R&D group at Verisign where he contributes to Internet standards and builds proof of concepts exploring new products and technologies. After more than 25 years in the industry Glen brings a rich blend of history and hands on experience to the talk.

" }, - + { - + "serial": 6574, "name": "Eric Wilhelm", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_6574.jpg", @@ -12844,9 +12844,9 @@ "twitter": "", "bio": "

Eric Wilhelm is a programmer at Cisco, Inc. He is a father of two, former leader of the Portland Perl Mongers, author of many CPAN modules, and a contributor to several open source projects. He has spoken numerous times at OSCON and local user groups.

" }, - + { - + "serial": 182808, "name": "Kjerstin Williams", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_182808.jpg", @@ -12856,9 +12856,9 @@ "twitter": "kjkjerstin", "bio": "

Dr. Kjerstin ‘KJ’ Williams directs the robotics and intelligent systems efforts at Applied Minds in Glendale, California. Kjerstin has delivered a wide variety of invited lectures and demonstrations on topics ranging from biologically-inspired design to computer vision in venues ranging from academic conferences to science museums
\nto the main stage at O’Reilly Media’s Maker Faire. Her current research interests include multi-modal perception strategies and the design and control of truly field-deployable, intelligent systems.

\n

Also, she\u2019s a fantastic jazz singer with KJ and the Conspirators in Los Angeles.

" }, - + { - + "serial": 183908, "name": "Cedric Williams", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_183908.jpg", @@ -12868,9 +12868,9 @@ "twitter": "AskCedricW", "bio": "

I am a technologist, advocate, and coach. I aspire to nurture products, businesses, and societies that make a difference in people’s lives.

" }, - + { - + "serial": 122424, "name": "John Willis", "photo": null, @@ -12880,9 +12880,9 @@ "twitter": null, "bio": "

John Willis is the VP of Customer Enablement for Stateless Networks. Willis, a 30-year systems management veteran, joined Stateless Networks from Dell where he was Chief DevOps evangelist. Willis, a noted expert on agile philosophies in systems management, came to Dell as part of their Enstratius acquisition. At Enstratius, Willis was the VP of Customer Enablement responsible for product support and services for the multi-cloud management platform. During his career, he has held positions at Opscode and also founded Gulf Breeze Software, an award-winning IBM business partner specializing in deploying Tivoli technology for the enterprise. Willis has authored six IBM Redbooks for IBM on enterprise systems management and was the founder and chief architect at Chain Bridge Systems. He is also co author of the \u201cDevops Cookbook\u201d and the upcoming \u201cNetwork Operations\u201d published by O\u201dReilly.

" }, - + { - + "serial": 150073, "name": "Mike Wolfson", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_150073.jpg", @@ -12892,9 +12892,9 @@ "twitter": "mikewolfson", "bio": "

Mike is a passionate mobile designer/developer working out of Phoenix. He has been working in the software field for more than 15 years, and with Android since its introduction. Currently, he develops Android applications for the health care field, and is a Lead Mobile Developer at Athenahealth\\Epocrates. He has a few successful apps in the Market and is an active contributor to the tech community, including organizing the local GDG.

\n

Mike has spoken about Android and mobile development at a variety of conferences and user groups (including Oscon). When he is not geeking out about phones, he enjoys the outdoors (snowboarding, hiking, scuba diving), collecting PEZ dispensers, and chasing his young (but quick) daughter.

" }, - + { - + "serial": 175481, "name": "Jeff Wolski", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_175481.jpg", @@ -12904,9 +12904,9 @@ "twitter": "", "bio": "

Jeff Wolski has over 10 years of experience in tech and has worked in a variety of environments: investment banking, hospital operating rooms, broadcast television graphics, flash sale retailers and now Uber. At Uber, he spends his time hacking on Node.js, learning how to Scala, building Graphite dashboards and drinking jasmine green tea. He originally hails from New York and has recently made the trek over to San Francisco, a city which he finds amazingly beautiful.

" }, - + { - + "serial": 153565, "name": "Fangjin Yang", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_153565.jpg", @@ -12916,9 +12916,9 @@ "twitter": null, "bio": "

Fangjin is one of the main Druid contributors and one of the first developers to Metamarkets. He mainly works on core infrastructure and platform development. Fangjin comes to Metamarkets from Cisco where he developed diagnostic algorithms for various routers and switches. He holds a BASc in Electrical Engineering and a MASc in Computer Engineering from the University of Waterloo, Canada.

" }, - + { - + "serial": 173466, "name": "Alex Yong", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173466.jpg", @@ -12928,9 +12928,9 @@ "twitter": "", "bio": "

Alex is a graduating senior at Saint Joseph’s College, where he is majoring in Computer Science. After graduation, he has accepted a position working in network operations at Indiana University.

" }, - + { - + "serial": 171450, "name": "Danny Yuan", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_171450.jpg", @@ -12940,9 +12940,9 @@ "twitter": "g9yuayon", "bio": "

Danny is an architect and software developer in Netflix’s Platform Engineering team. He works on Netflix’s distributed crypto service, data pipeline, and real-time analytics. He is the owner of Netflix’s open sourced data pipeline, Suro, and also the owner of Netflix’s predictive autoscaling engine.

" }, - + { - + "serial": 169932, "name": "Tobias Zander", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_169932.jpg", @@ -12952,9 +12952,9 @@ "twitter": "airbone42", "bio": "

Tobias is the CTO and a partner at Sitewards in Frankfurt, who specialize in e-commerce solutions.
\nPreviously he was well regarded as a freelance consultant and software architect. Over the past years he has built up a development team at Sitewards that thrives to be at the cutting edge of web development.
\nWith passion of inspiring developers he has taken part in and spoken at conferences such as Meet Magento, Developers Paradise, IPC, User groups and Unconferences. He has also had articles published in t3n and PHPMagazin.

" }, - + { - + "serial": 173468, "name": "Danilo Zekovic", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_173468.jpg", @@ -12964,9 +12964,9 @@ "twitter": "", "bio": "

Danilo is a sophomore at Saint Joseph’s College, from Novi Sad, Serbia. His interests are web programming, teaching programming, and anything in general that involves programming.

" }, - + { - + "serial": 141590, "name": "Carina C. Zona", "photo": "http://cdn.oreillystatic.com/en/assets/1/eventprovider/1/_@user_141590.jpg", @@ -12976,332 +12976,332 @@ "twitter": "cczona", "bio": "

Carina C. Zona is a developer and advocate. Her day job is as the community manager for open source software ZeroVM. She has also been a teacher & organizer for many tech women\u2019s organizations. Carina is the founder of @CallbackWomen, an initiative to connect first-time speakers with conferences. She is also a certified sex educator. In her spare time, she engineers baked goods. Using git. Because she loves versioning that much.

" } - + ], "venues": [ - + { "serial": 1448, "name": "Portland Ballroom", "category": "Conference Venues" }, - + { "serial": 1449, "name": "Portland 251", "category": "Conference Venues" }, - + { "serial": 1450, "name": "Portland 252", "category": "Conference Venues" }, - + { "serial": 1452, "name": "Portland 255", "category": "Conference Venues" }, - + { "serial": 1475, "name": "Portland 256", "category": "Conference Venues" }, - + { "serial": 1454, "name": "D135", "category": "Conference Venues" }, - + { "serial": 1456, "name": "D136", "category": "Conference Venues" }, - + { "serial": 1457, "name": "D137/138", "category": "Conference Venues" }, - + { "serial": 1458, "name": "D139/140", "category": "Conference Venues" }, - + { "serial": 1470, "name": "E143/144", "category": "Conference Venues" }, - + { "serial": 1464, "name": "E144", "category": "Conference Venues" }, - + { "serial": 1465, "name": "E145", "category": "Conference Venues" }, - + { "serial": 1471, "name": "E145/146", "category": "Conference Venues" }, - + { "serial": 1466, "name": "E146", "category": "Conference Venues" }, - + { "serial": 1451, "name": "E147/148", "category": "Conference Venues" }, - + { "serial": 1587, "name": "D133/135", "category": "Conference Venues" }, - + { "serial": 1607, "name": "E 141/142", "category": "Conference Venues" }, - + { "serial": 1459, "name": "F150", "category": "Conference Venues" }, - + { "serial": 1462, "name": "F151", "category": "Conference Venues" }, - + { "serial": 1460, "name": "E141", "category": "Conference Venues" }, - + { "serial": 1461, "name": "E142", "category": "Conference Venues" }, - + { "serial": 1463, "name": "E143", "category": "Conference Venues" }, - + { "serial": 1507, "name": "D130", "category": "Conference Venues" }, - + { "serial": 1520, "name": " D135", "category": "Conference Venues" }, - + { "serial": 1525, "name": "Portland Ballroom", "category": "Conference Venues" }, - + { "serial": 1467, "name": "Exhibit Hall D", "category": "Conference Venues" }, - + { "serial": 1469, "name": "Exhibit Hall C", "category": "Conference Venues" }, - + { "serial": 1468, "name": "Exhibit Hall E", "category": "Conference Venues" }, - + { "serial": 1453, "name": "E147 / E148", "category": "Conference Venues" }, - + { "serial": 1473, "name": "Portland Ballroom Foyer", "category": "Conference Venues" }, - + { "serial": 1474, "name": "Expo Hall", "category": "Conference Venues" }, - + { "serial": 1522, "name": "See BoF Schedule Onsite for Locations", "category": "Conference Venues" }, - + { "serial": 1521, "name": "Offsite", "category": "Conference Venues" }, - + { "serial": 1523, "name": "On Your Own", "category": "Conference Venues" }, - + { "serial": 1524, "name": "Expo Hall", "category": "Conference Venues" }, - + { "serial": 1626, "name": "Puppet Labs Headquarters, 926 Northwest 13th Avenue, #210", "category": "Conference Venues" }, - + { "serial": 1526, "name": "Exhibit Hall D", "category": "Conference Venues" }, - + { "serial": 1546, "name": "Expo Hall (Table A)", "category": "Conference Venues" }, - + { "serial": 1547, "name": "Expo Hall (Table B)", "category": "Conference Venues" }, - + { "serial": 1548, "name": "Expo Hall (Table C)", "category": "Conference Venues" }, - + { "serial": 1579, "name": "Expo Hall (Table D)", "category": "Conference Venues" }, - + { "serial": 1580, "name": "Expo Hall (Table E)", "category": "Conference Venues" }, - + { "serial": 1596, "name": "Office Hours Expo Hall", "category": "Conference Venues" }, - + { "serial": 1597, "name": "Author Signings (O'Reilly Booth) ", "category": "Conference Venues" }, - + { "serial": 1549, "name": "Author Signing A", "category": "Conference Venues" }, - + { "serial": 1606, "name": "123 NE Third Ave.", "category": "Conference Venues" }, - + { "serial": 1550, "name": "Author Signing B", "category": "Conference Venues" }, - + { "serial": 1598, "name": "Author Signing C", "category": "Conference Venues" }, - + { "serial": 1583, "name": " Union Pine, 525 SE Pine St.", "category": "Conference Venues" }, - + { "serial": 1584, "name": "F 150", "category": "Conference Venues" }, - + { "serial": 1585, "name": "Exhibit Hall B", "category": "Conference Venues" }, - + { "serial": 1574, "name": "Bottom of the stairs by the E Rooms", "category": "Conference Venues" }, - + { "serial": 1578, "name": "Jupiter Hotel", "category": "Conference Venues" } - + ] }} - + diff --git a/25-class-metaprog/checked/decorator/checkeddeco.py b/25-class-metaprog/checked/decorator/checkeddeco.py index 401835d..fa6abd3 100644 --- a/25-class-metaprog/checked/decorator/checkeddeco.py +++ b/25-class-metaprog/checked/decorator/checkeddeco.py @@ -96,7 +96,7 @@ def checked(cls: type) -> type: # <1> for name, constructor in _fields(cls).items(): # <2> setattr(cls, name, Field(name, constructor)) # <3> - cls._fields = classmethod(_fields) #type: ignore # <4> + cls._fields = classmethod(_fields) # type: ignore # <4> instance_methods = ( # <5> __init__, From bd7d78deccb4d0b9e86d1729530b9d721d96939a Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 01:00:30 -0300 Subject: [PATCH 027/127] update urllib3 due to GHSA-q2q7-5pp4-w6pg --- 21-futures/getflags/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/21-futures/getflags/requirements.txt b/21-futures/getflags/requirements.txt index 447f2e9..793679b 100644 --- a/21-futures/getflags/requirements.txt +++ b/21-futures/getflags/requirements.txt @@ -5,7 +5,7 @@ certifi==2020.12.5 chardet==4.0.0 idna==2.10 requests==2.25.1 -urllib3==1.26.4 +urllib3==1.26.5 tqdm==4.56.2 multidict==5.1.0 yarl==1.6.3 From e1121adf61a72b4df92a0891712225aaeb1ab4da Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 01:03:07 -0300 Subject: [PATCH 028/127] Update README.md --- 18-context-mngr/lispy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/18-context-mngr/lispy/README.md b/18-context-mngr/lispy/README.md index 1a2acfb..4f3f75d 100644 --- a/18-context-mngr/lispy/README.md +++ b/18-context-mngr/lispy/README.md @@ -6,7 +6,7 @@ for Scheme. * `original/`: Norvig's `lis.py` unchanged, `lispy.py` with [minor changes](https://github.com/norvig/pytudes/pull/106) to run on Python 3, -and the `lispytest.py` custom test suite; +and the `lispytest.py` custom test script; * `py3.9/`: `lis.py` with type hints and a few minor edits—requires Python 3.9; * `py3.10/`: `lis.py` with type hints, pattern matching, and minor edits—requires Python 3.10. From df84fe38aa3967dec733a04e68f15bcb56e0c126 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 01:08:40 -0300 Subject: [PATCH 029/127] Update README.md --- README.md | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 7d786e1..2cfa562 100644 --- a/README.md +++ b/README.md @@ -18,36 +18,36 @@ New chapters in **Fluent Python 2e** are marked with 🆕. 🚨 This table of contents is subject to change at any time until the book goes to the printer. -Part / Chapter #|Title|Directory|Notebook|1st ed. Chapter # +Part / Chapter #|Title|Directory|1st ed. Chapter # ---:|---|---|---|:---: **I – Prologue**| -1|The Python Data Model|[01-data-model](01-data-model)|[data-model.ipynb](01-data-model/data-model.ipynb)|1 +1|The Python Data Model|[01-data-model](01-data-model)|1 **II – Data Structures**| -2|An Array of Sequences|[02-array-seq](02-array-seq)|[array-seq.ipynb](02-array-seq/array-seq.ipynb)|2 -3|Dictionaries and Sets|[03-dict-set](03-dict-set)||3 -4|Text versus Bytes|[04-text-byte](04-text-byte)||4 -🆕 5|Record-like Data Structures|[05-record-like](05-record-like)||– -6|Object References, Mutability, and Recycling|[06-obj-ref](06-obj-ref)||8 +2|An Array of Sequences|[02-array-seq](02-array-seq)|2 +3|Dictionaries and Sets|[03-dict-set](03-dict-set)|3 +4|Text versus Bytes|[04-text-byte](04-text-byte)|4 +5|Record-like Data Structures|[05-record-like](05-record-like)|🆕 +6|Object References, Mutability, and Recycling|[06-obj-ref](06-obj-ref)|8 **III – Functions as Objects**| -7|First-Class Funcions|[07-1class-func](07-1class-func)||5 -🆕 8|Type Hints in Function Definitions|[08-def-type-hints](08-def-type-hints)||– -9|Function Decorators and Closures|[09-closure-deco](09-closure-deco)||7 -10|Design Patterns with First-Class Functions|[10-dp-1class-func](10-dp-1class-func)||6 +7|First-Class Funcions|[07-1class-func](07-1class-func)|5 +8|Type Hints in Function Definitions|[08-def-type-hints](08-def-type-hints)|🆕 +9|Function Decorators and Closures|[09-closure-deco](09-closure-deco)|7 +10|Design Patterns with First-Class Functions|[10-dp-1class-func](10-dp-1class-func)|6 **IV – Object-Oriented Idioms**| -11|A Pythonic Object|[11-pythonic-obj](11-pythonic-obj)||9 -12|Sequence Hacking, Hashing, and Slicing|[12-seq-hacking](12-seq-hacking)||10 -13|Interfaces, Protocols, and ABCs|[13-protocl-abc](13-protocol-abc)||11 -14|Inheritance: For Good or For Worse|[14-inheritance](14-inheritance)||12 -🆕 15|More About Type Hints|[15-more-types](15-more-types)||– -16|Operator Overloading: Doing It Right|[16-op-overloading](16-op-overloading)||13 +11|A Pythonic Object|[11-pythonic-obj](11-pythonic-obj)|9 +12|Sequence Hacking, Hashing, and Slicing|[12-seq-hacking](12-seq-hacking)|10 +13|Interfaces, Protocols, and ABCs|[13-protocl-abc](13-protocol-abc)|11 +14|Inheritance: For Good or For Worse|[14-inheritance](14-inheritance)|12 +15|More About Type Hints|[15-more-types](15-more-types)|🆕 +16|Operator Overloading: Doing It Right|[16-op-overloading](16-op-overloading)|13 **V – Control Flow**| -17|Iterables, Iterators, and Generators|[17-it-generator](17-it-generator)||14 -18|Context Managers and else Blocks|[18-context-mngr](18-context-mngr)||15 -19|Classic Coroutines|[19-coroutine](19-coroutine)||16 -🆕 20|Concurrency Models in Python|[20-concurrency](20-concurrency)||- -21|Concurrency with Futures|[21-futures](21-futures)||17 -22|Asynchronous Programming|[22-async](22-async)||18 +17|Iterables, Iterators, and Generators|[17-it-generator](17-it-generator)|14 +18|Context Managers and else Blocks|[18-context-mngr](18-context-mngr)|15 +19|Classic Coroutines|[19-coroutine](19-coroutine)|16 +20|Concurrency Models in Python|[20-concurrency](20-concurrency)|🆕 +21|Concurrency with Futures|[21-futures](21-futures)|17 +22|Asynchronous Programming|[22-async](22-async)|18 **VI – Metaprogramming**| -23|Dynamic Attributes and Properties|[22-dyn-attr-prop](22-dyn-attr-prop)||19 -24|Attribute Descriptors|[23-descriptor](23-descriptor)||20 -25|Class Metaprogramming|[24-class-metaprog](24-class-metaprog)||21 +23|Dynamic Attributes and Properties|[22-dyn-attr-prop](22-dyn-attr-prop)|19 +24|Attribute Descriptors|[23-descriptor](23-descriptor)|20 +25|Class Metaprogramming|[24-class-metaprog](24-class-metaprog)|21 From 378a82b470ed18c18243efb4cc91d74090db5469 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 01:10:59 -0300 Subject: [PATCH 030/127] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2cfa562..8149e80 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ New chapters in **Fluent Python 2e** are marked with 🆕. 🚨 This table of contents is subject to change at any time until the book goes to the printer. Part / Chapter #|Title|Directory|1st ed. Chapter # ----:|---|---|---|:---: +---:|---|---|:---: **I – Prologue**| 1|The Python Data Model|[01-data-model](01-data-model)|1 **II – Data Structures**| From 0f9c2970e00ca8d6aa952f11be8c0877cd0954db Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 01:13:17 -0300 Subject: [PATCH 031/127] Update README.md --- 18-context-mngr/lispy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/18-context-mngr/lispy/README.md b/18-context-mngr/lispy/README.md index 4f3f75d..2a3ef13 100644 --- a/18-context-mngr/lispy/README.md +++ b/18-context-mngr/lispy/README.md @@ -15,7 +15,7 @@ The `py3.9/` and `py3.10/` directories also have identical `lis_test.py` to run These files include all the [`lis_tests` suite](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/py/lispytest.py#L5) from `original/lispytest.py`, -and individual tests for each expression and special form handled by `evaluate`. +and separate tests for each expression and special form handled by `evaluate`. ## Provenance, Copyright and License From 009b92950f32710dfe79f0e2bde544c7a35d16e7 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 01:21:44 -0300 Subject: [PATCH 032/127] Update README.md --- 18-context-mngr/lispy/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/18-context-mngr/lispy/README.md b/18-context-mngr/lispy/README.md index 2a3ef13..0f98ccd 100644 --- a/18-context-mngr/lispy/README.md +++ b/18-context-mngr/lispy/README.md @@ -22,3 +22,5 @@ and separate tests for each expression and special form handled by `evaluate`. `lis.py` is published in the [norvig/pytudes](https://github.com/norvig/pytudes) repository on Github. The copyright holder is Peter Norvig and the code is licensed under the [MIT license](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/LICENSE). + +Luciano Ramalho wrote the changes and additions described above. From 481f6b99ef5d0795474ff215bb593133da6ad23f Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 01:22:31 -0300 Subject: [PATCH 033/127] Update README.md --- 18-context-mngr/lispy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/18-context-mngr/lispy/README.md b/18-context-mngr/lispy/README.md index 0f98ccd..7edb1fa 100644 --- a/18-context-mngr/lispy/README.md +++ b/18-context-mngr/lispy/README.md @@ -23,4 +23,4 @@ and separate tests for each expression and special form handled by `evaluate`. The copyright holder is Peter Norvig and the code is licensed under the [MIT license](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/LICENSE). -Luciano Ramalho wrote the changes and additions described above. +I, Luciano Ramalho, wrote the changes and additions described above. From 67c3f46dc2c3ebbd3460d3ade46d72a35b9b513c Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 01:46:20 -0300 Subject: [PATCH 034/127] ch18: added std_env fixture to lis.py tests --- 18-context-mngr/lispy/py3.10/lis_test.py | 46 +++++++++++------------- 18-context-mngr/lispy/py3.9/lis_test.py | 46 +++++++++++------------- 2 files changed, 42 insertions(+), 50 deletions(-) diff --git a/18-context-mngr/lispy/py3.10/lis_test.py b/18-context-mngr/lispy/py3.10/lis_test.py index 591046d..f9c96ae 100755 --- a/18-context-mngr/lispy/py3.10/lis_test.py +++ b/18-context-mngr/lispy/py3.10/lis_test.py @@ -1,6 +1,6 @@ from typing import Optional -from pytest import mark +from pytest import mark, fixture from lis import parse, evaluate, Expression, Environment, standard_env @@ -39,12 +39,15 @@ ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), ]) -@mark.skip def test_evaluate(source: str, expected: Optional[Expression]) -> None: got = evaluate(parse(source)) assert got == expected +@fixture +def std_env() -> Environment: + return standard_env() + # tests for each of the cases in evaluate def test_evaluate_variable() -> None: @@ -83,65 +86,58 @@ def test_evaluate_if_false() -> None: assert got == expected -def test_define() -> None: - env: Environment = standard_env() +def test_define(std_env: Environment) -> None: source = '(define answer (* 6 7))' - got = evaluate(parse(source), env) + got = evaluate(parse(source), std_env) assert got is None - assert env['answer'] == 42 + assert std_env['answer'] == 42 -def test_lambda() -> None: - env: Environment = standard_env() +def test_lambda(std_env: Environment) -> None: source = '(lambda (a b) (if (>= a b) a b))' - func = evaluate(parse(source), env) + func = evaluate(parse(source), std_env) assert func.parms == ['a', 'b'] assert func.body == ['if', ['>=', 'a', 'b'], 'a', 'b'] - assert func.env is env + assert func.env is std_env assert func(1, 2) == 2 assert func(3, 2) == 3 -def test_begin() -> None: - env: Environment = standard_env() +def test_begin(std_env: Environment) -> None: source = """ (begin (define x (* 2 3)) (* x 7) ) """ - got = evaluate(parse(source), env) + got = evaluate(parse(source), std_env) assert got == 42 -def test_invocation_builtin_car() -> None: - env: Environment = standard_env() +def test_invocation_builtin_car(std_env: Environment) -> None: source = '(car (quote (11 22 33)))' - got = evaluate(parse(source), env) + got = evaluate(parse(source), std_env) assert got == 11 -def test_invocation_builtin_append() -> None: - env: Environment = standard_env() +def test_invocation_builtin_append(std_env: Environment) -> None: source = '(append (quote (a b)) (quote (c d)))' - got = evaluate(parse(source), env) + got = evaluate(parse(source), std_env) assert got == ['a', 'b', 'c', 'd'] -def test_invocation_builtin_map() -> None: - env: Environment = standard_env() +def test_invocation_builtin_map(std_env: Environment) -> None: source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))' - got = evaluate(parse(source), env) + got = evaluate(parse(source), std_env) assert got == [2, 4, 6] -def test_invocation_user_procedure() -> None: - env: Environment = standard_env() +def test_invocation_user_procedure(std_env: Environment) -> None: source = """ (begin (define max (lambda (a b) (if (>= a b) a b))) (max 22 11) ) """ - got = evaluate(parse(source), env) + got = evaluate(parse(source), std_env) assert got == 22 diff --git a/18-context-mngr/lispy/py3.9/lis_test.py b/18-context-mngr/lispy/py3.9/lis_test.py index 591046d..f9c96ae 100755 --- a/18-context-mngr/lispy/py3.9/lis_test.py +++ b/18-context-mngr/lispy/py3.9/lis_test.py @@ -1,6 +1,6 @@ from typing import Optional -from pytest import mark +from pytest import mark, fixture from lis import parse, evaluate, Expression, Environment, standard_env @@ -39,12 +39,15 @@ ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), ]) -@mark.skip def test_evaluate(source: str, expected: Optional[Expression]) -> None: got = evaluate(parse(source)) assert got == expected +@fixture +def std_env() -> Environment: + return standard_env() + # tests for each of the cases in evaluate def test_evaluate_variable() -> None: @@ -83,65 +86,58 @@ def test_evaluate_if_false() -> None: assert got == expected -def test_define() -> None: - env: Environment = standard_env() +def test_define(std_env: Environment) -> None: source = '(define answer (* 6 7))' - got = evaluate(parse(source), env) + got = evaluate(parse(source), std_env) assert got is None - assert env['answer'] == 42 + assert std_env['answer'] == 42 -def test_lambda() -> None: - env: Environment = standard_env() +def test_lambda(std_env: Environment) -> None: source = '(lambda (a b) (if (>= a b) a b))' - func = evaluate(parse(source), env) + func = evaluate(parse(source), std_env) assert func.parms == ['a', 'b'] assert func.body == ['if', ['>=', 'a', 'b'], 'a', 'b'] - assert func.env is env + assert func.env is std_env assert func(1, 2) == 2 assert func(3, 2) == 3 -def test_begin() -> None: - env: Environment = standard_env() +def test_begin(std_env: Environment) -> None: source = """ (begin (define x (* 2 3)) (* x 7) ) """ - got = evaluate(parse(source), env) + got = evaluate(parse(source), std_env) assert got == 42 -def test_invocation_builtin_car() -> None: - env: Environment = standard_env() +def test_invocation_builtin_car(std_env: Environment) -> None: source = '(car (quote (11 22 33)))' - got = evaluate(parse(source), env) + got = evaluate(parse(source), std_env) assert got == 11 -def test_invocation_builtin_append() -> None: - env: Environment = standard_env() +def test_invocation_builtin_append(std_env: Environment) -> None: source = '(append (quote (a b)) (quote (c d)))' - got = evaluate(parse(source), env) + got = evaluate(parse(source), std_env) assert got == ['a', 'b', 'c', 'd'] -def test_invocation_builtin_map() -> None: - env: Environment = standard_env() +def test_invocation_builtin_map(std_env: Environment) -> None: source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))' - got = evaluate(parse(source), env) + got = evaluate(parse(source), std_env) assert got == [2, 4, 6] -def test_invocation_user_procedure() -> None: - env: Environment = standard_env() +def test_invocation_user_procedure(std_env: Environment) -> None: source = """ (begin (define max (lambda (a b) (if (>= a b) a b))) (max 22 11) ) """ - got = evaluate(parse(source), env) + got = evaluate(parse(source), std_env) assert got == 22 From dab0e72ec4355294ee8d2a65ac899e849eaf0e7c Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 01:47:22 -0300 Subject: [PATCH 035/127] ch18: added std_env fixture to lis.py tests --- 18-context-mngr/lispy/py3.10/mypy.ini | 2 ++ 18-context-mngr/lispy/py3.9/mypy.ini | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 18-context-mngr/lispy/py3.10/mypy.ini create mode 100644 18-context-mngr/lispy/py3.9/mypy.ini diff --git a/18-context-mngr/lispy/py3.10/mypy.ini b/18-context-mngr/lispy/py3.10/mypy.ini new file mode 100644 index 0000000..3456561 --- /dev/null +++ b/18-context-mngr/lispy/py3.10/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +python_version = 3.10 diff --git a/18-context-mngr/lispy/py3.9/mypy.ini b/18-context-mngr/lispy/py3.9/mypy.ini new file mode 100644 index 0000000..6fcaf90 --- /dev/null +++ b/18-context-mngr/lispy/py3.9/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +python_version = 3.9 From fb729033fc8eafef0f6f537c77d4d9baae1d69be Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 13:59:12 -0300 Subject: [PATCH 036/127] Update README.md --- 18-context-mngr/lispy/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/18-context-mngr/lispy/README.md b/18-context-mngr/lispy/README.md index 7edb1fa..8c5fe94 100644 --- a/18-context-mngr/lispy/README.md +++ b/18-context-mngr/lispy/README.md @@ -23,4 +23,6 @@ and separate tests for each expression and special form handled by `evaluate`. The copyright holder is Peter Norvig and the code is licensed under the [MIT license](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/LICENSE). -I, Luciano Ramalho, wrote the changes and additions described above. +I wrote the changes and additions described above. + +— Luciano Ramalho (@ramalho), 2021-06-09. From ceb63130ebe9539464c8a622a18ee78f80efa3dd Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 13:59:51 -0300 Subject: [PATCH 037/127] Update README.md --- 18-context-mngr/lispy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/18-context-mngr/lispy/README.md b/18-context-mngr/lispy/README.md index 8c5fe94..735f8f9 100644 --- a/18-context-mngr/lispy/README.md +++ b/18-context-mngr/lispy/README.md @@ -25,4 +25,4 @@ The copyright holder is Peter Norvig and the code is licensed under the I wrote the changes and additions described above. -— Luciano Ramalho (@ramalho), 2021-06-09. +— Luciano Ramalho ([@ramalho](https://github.com/ramalho/)), 2021-06-09. From 73900de66b6e8fc15b07e575c52d6b4aeefaaa1c Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 14:03:24 -0300 Subject: [PATCH 038/127] Update README.md --- 18-context-mngr/lispy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/18-context-mngr/lispy/README.md b/18-context-mngr/lispy/README.md index 735f8f9..69e042c 100644 --- a/18-context-mngr/lispy/README.md +++ b/18-context-mngr/lispy/README.md @@ -15,7 +15,7 @@ The `py3.9/` and `py3.10/` directories also have identical `lis_test.py` to run These files include all the [`lis_tests` suite](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/py/lispytest.py#L5) from `original/lispytest.py`, -and separate tests for each expression and special form handled by `evaluate`. +and additional separate tests for each expression and special form handled by `evaluate`. ## Provenance, Copyright and License From b0a258da9df045040af783e2b344c9b7612ca8bd Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 14:05:19 -0300 Subject: [PATCH 039/127] Update README.md --- 18-context-mngr/lispy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/18-context-mngr/lispy/README.md b/18-context-mngr/lispy/README.md index 69e042c..cf18e3b 100644 --- a/18-context-mngr/lispy/README.md +++ b/18-context-mngr/lispy/README.md @@ -2,7 +2,7 @@ This directory contains 3 versions of [Peter Norvig's `lis.py` interpreter](https://norvig.com/lispy.html) -for Scheme. +for a subset of [Scheme](https://en.wikipedia.org/wiki/Scheme_(programming_language)). * `original/`: Norvig's `lis.py` unchanged, `lispy.py` with [minor changes](https://github.com/norvig/pytudes/pull/106) to run on Python 3, From 013a26295b71f7eda3663df011d2d6150fd65d07 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 14:05:43 -0300 Subject: [PATCH 040/127] Update README.md --- 18-context-mngr/lispy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/18-context-mngr/lispy/README.md b/18-context-mngr/lispy/README.md index cf18e3b..89526d5 100644 --- a/18-context-mngr/lispy/README.md +++ b/18-context-mngr/lispy/README.md @@ -1,6 +1,6 @@ # lis.py -This directory contains 3 versions of +This directory contains 4 versions of [Peter Norvig's `lis.py` interpreter](https://norvig.com/lispy.html) for a subset of [Scheme](https://en.wikipedia.org/wiki/Scheme_(programming_language)). From b4ffc549210c9f18de5e9979e1c91e65571b8173 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 14:06:00 -0300 Subject: [PATCH 041/127] Update README.md --- 18-context-mngr/lispy/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/18-context-mngr/lispy/README.md b/18-context-mngr/lispy/README.md index 89526d5..cf18e3b 100644 --- a/18-context-mngr/lispy/README.md +++ b/18-context-mngr/lispy/README.md @@ -1,6 +1,6 @@ # lis.py -This directory contains 4 versions of +This directory contains 3 versions of [Peter Norvig's `lis.py` interpreter](https://norvig.com/lispy.html) for a subset of [Scheme](https://en.wikipedia.org/wiki/Scheme_(programming_language)). From 2338cb9d9824ec882ff921e9f2f7119f776d9693 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 16:15:36 -0300 Subject: [PATCH 042/127] Update lis.py --- 18-context-mngr/lispy/py3.10/lis.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/18-context-mngr/lispy/py3.10/lis.py b/18-context-mngr/lispy/py3.10/lis.py index ca0f8e8..b5df2c0 100644 --- a/18-context-mngr/lispy/py3.10/lis.py +++ b/18-context-mngr/lispy/py3.10/lis.py @@ -25,7 +25,8 @@ def __init__(self, parms: list[str], body: Expression, env: Environment): self.parms, self.body, self.env = parms, body, env def __call__(self, *args: Expression) -> Any: - env: Environment = ChainMap(dict(zip(self.parms, args)), self.env) + local_env = dict(zip(self.parms, args)) + env: Environment = ChainMap(local_env, self.env) return evaluate(self.body, env) From 36afc017d4343b994732b177329cc8b3c1c1f47e Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 19:41:34 -0300 Subject: [PATCH 043/127] lis.py: removed 'global_env' as default for 'evaluate(x, env)' --- 18-context-mngr/lispy/py3.10/lis.py | 2 +- 18-context-mngr/lispy/py3.10/lis_test.py | 21 ++++++++++++--------- 18-context-mngr/lispy/py3.9/lis.py | 2 +- 18-context-mngr/lispy/py3.9/lis_test.py | 21 ++++++++++++--------- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/18-context-mngr/lispy/py3.10/lis.py b/18-context-mngr/lispy/py3.10/lis.py index b5df2c0..aa79411 100644 --- a/18-context-mngr/lispy/py3.10/lis.py +++ b/18-context-mngr/lispy/py3.10/lis.py @@ -139,7 +139,7 @@ def lispstr(exp: object) -> str: ################ eval -def evaluate(x: Expression, env: Environment = global_env) -> Any: +def evaluate(x: Expression, env: Environment) -> Any: "Evaluate an expression in an environment." match x: case str(): # variable reference diff --git a/18-context-mngr/lispy/py3.10/lis_test.py b/18-context-mngr/lispy/py3.10/lis_test.py index f9c96ae..55fd361 100755 --- a/18-context-mngr/lispy/py3.10/lis_test.py +++ b/18-context-mngr/lispy/py3.10/lis_test.py @@ -4,6 +4,9 @@ from lis import parse, evaluate, Expression, Environment, standard_env +# Norvig's tests are not isolated: they assume the +# same environment from first to last test. +ENV_FOR_FIRST_TEST = standard_env() @mark.parametrize( 'source, expected', [ ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), @@ -40,7 +43,7 @@ ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), ]) def test_evaluate(source: str, expected: Optional[Expression]) -> None: - got = evaluate(parse(source)) + got = evaluate(parse(source), ENV_FOR_FIRST_TEST) assert got == expected @@ -58,31 +61,31 @@ def test_evaluate_variable() -> None: assert got == expected -def test_evaluate_literal() -> None: +def test_evaluate_literal(std_env: Environment) -> None: source = '3.3' expected = 3.3 - got = evaluate(parse(source)) + got = evaluate(parse(source), std_env) assert got == expected -def test_evaluate_quote() -> None: +def test_evaluate_quote(std_env: Environment) -> None: source = '(quote (1.1 is not 1))' expected = [1.1, 'is', 'not', 1] - got = evaluate(parse(source)) + got = evaluate(parse(source), std_env) assert got == expected -def test_evaluate_if_true() -> None: +def test_evaluate_if_true(std_env: Environment) -> None: source = '(if 1 10 no-such-thing)' expected = 10 - got = evaluate(parse(source)) + got = evaluate(parse(source), std_env) assert got == expected -def test_evaluate_if_false() -> None: +def test_evaluate_if_false(std_env: Environment) -> None: source = '(if 0 no-such-thing 20)' expected = 20 - got = evaluate(parse(source)) + got = evaluate(parse(source), std_env) assert got == expected diff --git a/18-context-mngr/lispy/py3.9/lis.py b/18-context-mngr/lispy/py3.9/lis.py index fcedd1e..1843738 100644 --- a/18-context-mngr/lispy/py3.9/lis.py +++ b/18-context-mngr/lispy/py3.9/lis.py @@ -138,7 +138,7 @@ def lispstr(exp: object) -> str: ################ eval -def evaluate(x: Expression, env: Environment = global_env) -> Any: +def evaluate(x: Expression, env: Environment) -> Any: "Evaluate an expression in an environment." if isinstance(x, str): # variable reference return env[x] diff --git a/18-context-mngr/lispy/py3.9/lis_test.py b/18-context-mngr/lispy/py3.9/lis_test.py index f9c96ae..55fd361 100755 --- a/18-context-mngr/lispy/py3.9/lis_test.py +++ b/18-context-mngr/lispy/py3.9/lis_test.py @@ -4,6 +4,9 @@ from lis import parse, evaluate, Expression, Environment, standard_env +# Norvig's tests are not isolated: they assume the +# same environment from first to last test. +ENV_FOR_FIRST_TEST = standard_env() @mark.parametrize( 'source, expected', [ ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), @@ -40,7 +43,7 @@ ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), ]) def test_evaluate(source: str, expected: Optional[Expression]) -> None: - got = evaluate(parse(source)) + got = evaluate(parse(source), ENV_FOR_FIRST_TEST) assert got == expected @@ -58,31 +61,31 @@ def test_evaluate_variable() -> None: assert got == expected -def test_evaluate_literal() -> None: +def test_evaluate_literal(std_env: Environment) -> None: source = '3.3' expected = 3.3 - got = evaluate(parse(source)) + got = evaluate(parse(source), std_env) assert got == expected -def test_evaluate_quote() -> None: +def test_evaluate_quote(std_env: Environment) -> None: source = '(quote (1.1 is not 1))' expected = [1.1, 'is', 'not', 1] - got = evaluate(parse(source)) + got = evaluate(parse(source), std_env) assert got == expected -def test_evaluate_if_true() -> None: +def test_evaluate_if_true(std_env: Environment) -> None: source = '(if 1 10 no-such-thing)' expected = 10 - got = evaluate(parse(source)) + got = evaluate(parse(source), std_env) assert got == expected -def test_evaluate_if_false() -> None: +def test_evaluate_if_false(std_env: Environment) -> None: source = '(if 0 no-such-thing 20)' expected = 20 - got = evaluate(parse(source)) + got = evaluate(parse(source), std_env) assert got == expected From d88b17c75fa51af85deacd8679e3a512b7effb8a Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 19:58:43 -0300 Subject: [PATCH 044/127] lis.py: explicit var in first match/case --- 18-context-mngr/lispy/py3.10/lis.py | 4 ++-- 18-context-mngr/lispy/py3.10/lis_test.py | 29 ++++++++++++++---------- 18-context-mngr/lispy/py3.9/lis_test.py | 29 ++++++++++++++---------- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/18-context-mngr/lispy/py3.10/lis.py b/18-context-mngr/lispy/py3.10/lis.py index aa79411..160d6ec 100644 --- a/18-context-mngr/lispy/py3.10/lis.py +++ b/18-context-mngr/lispy/py3.10/lis.py @@ -142,8 +142,8 @@ def lispstr(exp: object) -> str: def evaluate(x: Expression, env: Environment) -> Any: "Evaluate an expression in an environment." match x: - case str(): # variable reference - return env[x] + case str(var): # variable reference + return env[var] case literal if not isinstance(x, list): # constant literal return literal case ['quote', exp]: # (quote exp) diff --git a/18-context-mngr/lispy/py3.10/lis_test.py b/18-context-mngr/lispy/py3.10/lis_test.py index 55fd361..44c3402 100755 --- a/18-context-mngr/lispy/py3.10/lis_test.py +++ b/18-context-mngr/lispy/py3.10/lis_test.py @@ -14,30 +14,35 @@ ("(+ (* 2 100) (* 1 10))", 210), ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), - ("(define x 3)", None), ("x", 3), ("(+ x x)", 6), + ("(define x 3)", None), + ("x", 3), + ("(+ x x)", 6), ("((lambda (x) (+ x x)) 5)", 10), - ("(define twice (lambda (x) (* 2 x)))", None), ("(twice 5)", 10), + ("(define twice (lambda (x) (* 2 x)))", None), + ("(twice 5)", 10), ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), ("((compose list twice) 5)", [10]), ("(define repeat (lambda (f) (compose f f)))", None), - ("((repeat twice) 5)", 20), ("((repeat (repeat twice)) 5)", 80), + ("((repeat twice) 5)", 20), + ("((repeat (repeat twice)) 5)", 80), ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), ("(fact 3)", 6), ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), ("""(define combine (lambda (f) - (lambda (x y) - (if (null? x) (quote ()) - (f (list (car x) (car y)) - ((combine f) (cdr x) (cdr y)))))))""", None), + (lambda (x y) + (if (null? x) (quote ()) + (f (list (car x) (car y)) + ((combine f) (cdr x) (cdr y)))))))""", None), ("(define zip (combine cons))", None), ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), - ("""(define riff-shuffle (lambda (deck) (begin - (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) - (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) - (define mid (lambda (seq) (/ (length seq) 2))) - ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), + ("""(define riff-shuffle (lambda (deck) + (begin + (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) + (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) + (define mid (lambda (seq) (/ (length seq) 2))) + ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), diff --git a/18-context-mngr/lispy/py3.9/lis_test.py b/18-context-mngr/lispy/py3.9/lis_test.py index 55fd361..44c3402 100755 --- a/18-context-mngr/lispy/py3.9/lis_test.py +++ b/18-context-mngr/lispy/py3.9/lis_test.py @@ -14,30 +14,35 @@ ("(+ (* 2 100) (* 1 10))", 210), ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), - ("(define x 3)", None), ("x", 3), ("(+ x x)", 6), + ("(define x 3)", None), + ("x", 3), + ("(+ x x)", 6), ("((lambda (x) (+ x x)) 5)", 10), - ("(define twice (lambda (x) (* 2 x)))", None), ("(twice 5)", 10), + ("(define twice (lambda (x) (* 2 x)))", None), + ("(twice 5)", 10), ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), ("((compose list twice) 5)", [10]), ("(define repeat (lambda (f) (compose f f)))", None), - ("((repeat twice) 5)", 20), ("((repeat (repeat twice)) 5)", 80), + ("((repeat twice) 5)", 20), + ("((repeat (repeat twice)) 5)", 80), ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), ("(fact 3)", 6), ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), ("""(define combine (lambda (f) - (lambda (x y) - (if (null? x) (quote ()) - (f (list (car x) (car y)) - ((combine f) (cdr x) (cdr y)))))))""", None), + (lambda (x y) + (if (null? x) (quote ()) + (f (list (car x) (car y)) + ((combine f) (cdr x) (cdr y)))))))""", None), ("(define zip (combine cons))", None), ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), - ("""(define riff-shuffle (lambda (deck) (begin - (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) - (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) - (define mid (lambda (seq) (/ (length seq) 2))) - ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), + ("""(define riff-shuffle (lambda (deck) + (begin + (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) + (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) + (define mid (lambda (seq) (/ (length seq) 2))) + ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), From 0cbb0c557cc73c2a01cd30bf8200b302cefe71e9 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 23:18:50 -0300 Subject: [PATCH 045/127] lis.py: added 'define' procedure short form --- 18-context-mngr/lispy/py3.10/lis.py | 24 +++++++++++++----------- 18-context-mngr/lispy/py3.10/lis_test.py | 18 ++++++++++++++++-- 18-context-mngr/lispy/py3.9/lis.py | 20 ++++++++++---------- 3 files changed, 39 insertions(+), 23 deletions(-) mode change 100755 => 100644 18-context-mngr/lispy/py3.10/lis_test.py diff --git a/18-context-mngr/lispy/py3.10/lis.py b/18-context-mngr/lispy/py3.10/lis.py index 160d6ec..6aacaf8 100644 --- a/18-context-mngr/lispy/py3.10/lis.py +++ b/18-context-mngr/lispy/py3.10/lis.py @@ -12,16 +12,17 @@ from collections.abc import MutableMapping from typing import Any, TypeAlias -Atom: TypeAlias = float | int | str +Symbol: TypeAlias = str +Atom: TypeAlias = float | int | Symbol Expression: TypeAlias = Atom | list -Environment: TypeAlias = MutableMapping[str, object] +Environment: TypeAlias = MutableMapping[Symbol, object] class Procedure: "A user-defined Scheme procedure." - def __init__(self, parms: list[str], body: Expression, env: Environment): + def __init__(self, parms: list[Symbol], body: Expression, env: Environment): self.parms, self.body, self.env = parms, body, env def __call__(self, *args: Expression) -> Any: @@ -68,14 +69,12 @@ def standard_env() -> Environment: 'number?': lambda x: isinstance(x, (int, float)), 'procedure?': callable, 'round': round, - 'symbol?': lambda x: isinstance(x, str), + 'symbol?': lambda x: isinstance(x, Symbol), } ) return env -global_env: Environment = standard_env() - ################ Parsing: parse, tokenize, and read_from_tokens @@ -114,7 +113,7 @@ def parse_atom(token: str) -> Atom: try: return float(token) except ValueError: - return str(token) + return Symbol(token) ################ Interaction: A REPL @@ -122,8 +121,9 @@ def parse_atom(token: str) -> Atom: def repl(prompt: str = 'lis.py> ') -> None: "A prompt-read-evaluate-print loop." + global_env: Environment = standard_env() while True: - val = evaluate(parse(input(prompt))) + val = evaluate(parse(input(prompt)), global_env) if val is not None: print(lispstr(val)) @@ -142,7 +142,7 @@ def lispstr(exp: object) -> str: def evaluate(x: Expression, env: Environment) -> Any: "Evaluate an expression in an environment." match x: - case str(var): # variable reference + case Symbol(var): # variable reference return env[var] case literal if not isinstance(x, list): # constant literal return literal @@ -151,9 +151,11 @@ def evaluate(x: Expression, env: Environment) -> Any: case ['if', test, conseq, alt]: # (if test conseq alt) exp = conseq if evaluate(test, env) else alt return evaluate(exp, env) - case ['define', var, exp]: # (define var exp) + case ['define', Symbol(var), exp]: # (define var exp) env[var] = evaluate(exp, env) - case ['lambda', parms, body]: # (lambda (var...) body) + case ['define', [name, *parms], body]: # (define (fun parm...) body) + env[name] = Procedure(parms, body, env) + case ['lambda', parms, body]: # (lambda (parm...) body) return Procedure(parms, body, env) case [op, *args]: # (proc arg...) proc = evaluate(op, env) diff --git a/18-context-mngr/lispy/py3.10/lis_test.py b/18-context-mngr/lispy/py3.10/lis_test.py old mode 100755 new mode 100644 index 44c3402..b2eb28a --- a/18-context-mngr/lispy/py3.10/lis_test.py +++ b/18-context-mngr/lispy/py3.10/lis_test.py @@ -6,7 +6,7 @@ # Norvig's tests are not isolated: they assume the # same environment from first to last test. -ENV_FOR_FIRST_TEST = standard_env() +global_env_for_first_test = standard_env() @mark.parametrize( 'source, expected', [ ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), @@ -48,7 +48,7 @@ ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), ]) def test_evaluate(source: str, expected: Optional[Expression]) -> None: - got = evaluate(parse(source), ENV_FOR_FIRST_TEST) + got = evaluate(parse(source), global_env_for_first_test) assert got == expected @@ -149,3 +149,17 @@ def test_invocation_user_procedure(std_env: Environment) -> None: """ got = evaluate(parse(source), std_env) assert got == 22 + + +###################################### for py3.10/lis.py only + +def test_define_function(std_env: Environment) -> None: + source = '(define (max a b) (if (>= a b) a b))' + got = evaluate(parse(source), std_env) + assert got is None + max_fn = std_env['max'] + assert max_fn.parms == ['a', 'b'] + assert max_fn.body == ['if', ['>=', 'a', 'b'], 'a', 'b'] + assert max_fn.env is std_env + assert max_fn(1, 2) == 2 + assert max_fn(3, 2) == 3 diff --git a/18-context-mngr/lispy/py3.9/lis.py b/18-context-mngr/lispy/py3.9/lis.py index 1843738..1a14935 100644 --- a/18-context-mngr/lispy/py3.9/lis.py +++ b/18-context-mngr/lispy/py3.9/lis.py @@ -12,20 +12,21 @@ from collections.abc import MutableMapping from typing import Union, Any -Atom = Union[float, int, str] +Symbol = str +Atom = Union[float, int, Symbol] Expression = Union[Atom, list] -Environment = MutableMapping[str, object] +Environment = MutableMapping[Symbol, object] class Procedure: "A user-defined Scheme procedure." - - def __init__(self, parms: list[str], body: Expression, env: Environment): + def __init__(self, parms: list[Symbol], body: Expression, env: Environment): self.parms, self.body, self.env = parms, body, env def __call__(self, *args: Expression) -> Any: - env: Environment = ChainMap(dict(zip(self.parms, args)), self.env) + local_env = dict(zip(self.parms, args)) + env: Environment = ChainMap(local_env, self.env) return evaluate(self.body, env) @@ -67,14 +68,12 @@ def standard_env() -> Environment: 'number?': lambda x: isinstance(x, (int, float)), 'procedure?': callable, 'round': round, - 'symbol?': lambda x: isinstance(x, str), + 'symbol?': lambda x: isinstance(x, Symbol), } ) return env -global_env: Environment = standard_env() - ################ Parsing: parse, tokenize, and read_from_tokens @@ -113,7 +112,7 @@ def parse_atom(token: str) -> Atom: try: return float(token) except ValueError: - return str(token) + return Symbol(token) ################ Interaction: A REPL @@ -121,8 +120,9 @@ def parse_atom(token: str) -> Atom: def repl(prompt: str = 'lis.py> ') -> None: "A prompt-read-evaluate-print loop." + global_env: Environment = standard_env() while True: - val = evaluate(parse(input(prompt))) + val = evaluate(parse(input(prompt)), global_env) if val is not None: print(lispstr(val)) From e5173fb72ba792189cfa852e4ea1e4d600a8d173 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Jun 2021 23:27:42 -0300 Subject: [PATCH 046/127] lis.py: added 'define' procedure short form --- 18-context-mngr/lispy/py3.10/lis.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/18-context-mngr/lispy/py3.10/lis.py b/18-context-mngr/lispy/py3.10/lis.py index 6aacaf8..628ddc7 100644 --- a/18-context-mngr/lispy/py3.10/lis.py +++ b/18-context-mngr/lispy/py3.10/lis.py @@ -142,22 +142,22 @@ def lispstr(exp: object) -> str: def evaluate(x: Expression, env: Environment) -> Any: "Evaluate an expression in an environment." match x: - case Symbol(var): # variable reference + case Symbol(var): # variable reference return env[var] - case literal if not isinstance(x, list): # constant literal + case literal if not isinstance(x, list): # constant literal return literal - case ['quote', exp]: # (quote exp) + case ['quote', exp]: # (quote exp) return exp - case ['if', test, conseq, alt]: # (if test conseq alt) + case ['if', test, conseq, alt]: # (if test conseq alt) exp = conseq if evaluate(test, env) else alt return evaluate(exp, env) - case ['define', Symbol(var), exp]: # (define var exp) + case ['lambda', parms, body]: # (lambda (parm...) body) + return Procedure(parms, body, env) + case ['define', Symbol(var), exp]: # (define var exp) env[var] = evaluate(exp, env) - case ['define', [name, *parms], body]: # (define (fun parm...) body) + case ['define', [name, *parms], body]: # (define (fun parm...) body) env[name] = Procedure(parms, body, env) - case ['lambda', parms, body]: # (lambda (parm...) body) - return Procedure(parms, body, env) - case [op, *args]: # (proc arg...) + case [op, *args]: # (proc arg...) proc = evaluate(op, env) values = (evaluate(arg, env) for arg in args) return proc(*values) From ca68e2e7e7dcd64efa978803e9931c092de352c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Jun 2021 15:57:02 +0000 Subject: [PATCH 047/127] build(deps): bump fastapi from 0.63.0 to 0.65.2 in /22-async/mojifinder Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.63.0 to 0.65.2. - [Release notes](https://github.com/tiangolo/fastapi/releases) - [Commits](https://github.com/tiangolo/fastapi/compare/0.63.0...0.65.2) --- updated-dependencies: - dependency-name: fastapi dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- 22-async/mojifinder/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/22-async/mojifinder/requirements.txt b/22-async/mojifinder/requirements.txt index 04ef58d..1f7c3d6 100644 --- a/22-async/mojifinder/requirements.txt +++ b/22-async/mojifinder/requirements.txt @@ -1,5 +1,5 @@ click==7.1.2 -fastapi==0.63.0 +fastapi==0.65.2 h11==0.12.0 pydantic==1.8.2 starlette==0.13.6 From 3606b1d411d0b81fc105984af8f587c035974b2e Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 17 Jun 2021 15:50:11 -0300 Subject: [PATCH 048/127] Update lis.py --- 18-context-mngr/lispy/py3.10/lis.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/18-context-mngr/lispy/py3.10/lis.py b/18-context-mngr/lispy/py3.10/lis.py index 628ddc7..ffd53c0 100644 --- a/18-context-mngr/lispy/py3.10/lis.py +++ b/18-context-mngr/lispy/py3.10/lis.py @@ -149,7 +149,10 @@ def evaluate(x: Expression, env: Environment) -> Any: case ['quote', exp]: # (quote exp) return exp case ['if', test, conseq, alt]: # (if test conseq alt) - exp = conseq if evaluate(test, env) else alt + if evaluate(test, env): + exp = conseq + else: + exp = alt return evaluate(exp, env) case ['lambda', parms, body]: # (lambda (parm...) body) return Procedure(parms, body, env) From f0f160844d097133597e5583eb47c53704a2447d Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 26 Jun 2021 13:42:28 -0300 Subject: [PATCH 049/127] sync with O'Reilly Atlas --- 02-array-seq/lispy/examples_test.py | 109 ++++++ .../py3.10 => 02-array-seq/lispy}/lis.py | 92 +++-- .../py3.10 => 02-array-seq/lispy}/lis_test.py | 17 + 02-array-seq/lispy/meta_test.py | 64 ++++ 02-array-seq/match_lat_lon.py | 32 ++ .../{metro_lat_long.py => metro_lat_lon.py} | 12 +- 04-text-byte/simplify.py | 2 +- 04-text-byte/zwj_sample.py | 3 +- 05-record-like/class/coordinates.py | 6 +- 05-record-like/dataclass/coordinates.py | 6 +- .../typing_namedtuple/coordinates.py | 8 +- .../typing_namedtuple/coordinates2.py | 6 +- .../typing_namedtuple/nocheck_demo.py | 2 +- 09-closure-deco/clockdeco.py | 8 +- 09-closure-deco/fibo_demo_cache.py | 2 +- 09-closure-deco/htmlizer.py | 4 +- 11-pythonic-obj/private/Confidential.java | 2 +- 18-context-mngr/lispy/LICENSE | 21 -- 18-context-mngr/lispy/README.md | 28 -- 18-context-mngr/lispy/original/lis.py | 132 -------- 18-context-mngr/lispy/original/lispy.py | 316 ------------------ 18-context-mngr/lispy/original/lispytest.py | 122 ------- 18-context-mngr/lispy/py3.10/mypy.ini | 2 - 18-context-mngr/lispy/py3.9/lis.py | 163 --------- 18-context-mngr/lispy/py3.9/lis_test.py | 151 --------- 18-context-mngr/lispy/py3.9/mypy.ini | 2 - 26 files changed, 308 insertions(+), 1004 deletions(-) create mode 100644 02-array-seq/lispy/examples_test.py rename {18-context-mngr/lispy/py3.10 => 02-array-seq/lispy}/lis.py (67%) rename {18-context-mngr/lispy/py3.10 => 02-array-seq/lispy}/lis_test.py (90%) create mode 100644 02-array-seq/lispy/meta_test.py create mode 100644 02-array-seq/match_lat_lon.py rename 02-array-seq/{metro_lat_long.py => metro_lat_lon.py} (61%) delete mode 100644 18-context-mngr/lispy/LICENSE delete mode 100644 18-context-mngr/lispy/README.md delete mode 100644 18-context-mngr/lispy/original/lis.py delete mode 100644 18-context-mngr/lispy/original/lispy.py delete mode 100644 18-context-mngr/lispy/original/lispytest.py delete mode 100644 18-context-mngr/lispy/py3.10/mypy.ini delete mode 100644 18-context-mngr/lispy/py3.9/lis.py delete mode 100755 18-context-mngr/lispy/py3.9/lis_test.py delete mode 100644 18-context-mngr/lispy/py3.9/mypy.ini diff --git a/02-array-seq/lispy/examples_test.py b/02-array-seq/lispy/examples_test.py new file mode 100644 index 0000000..38510a3 --- /dev/null +++ b/02-array-seq/lispy/examples_test.py @@ -0,0 +1,109 @@ +""" +Doctests for `parse`: + +>>> from lis import parse + +# tag::PARSE_DEMO[] +>>> parse('1.5') # <1> +1.5 +>>> parse('set!') # <2> +'set!' +>>> parse('(gcd 18 44)') # <3> +['gcd', 18, 44] +>>> parse('(- m (* n (// m n)))') # <4> +['-', 'm', ['*', 'n', ['//', 'm', 'n']]] + +# end::PARSE_DEMO[] + +""" + +import math + +from lis import run + + +fact_src = """ +(define (! n) + (if (< n 2) + 1 + (* n (! (- n 1))) + ) +) +(! 42) +""" +def test_factorial(): + got = run(fact_src) + assert got == 1405006117752879898543142606244511569936384000000000 + assert got == math.factorial(42) + + +gcd_src = """ +(define (mod m n) + (- m (* n (// m n)))) +(define (gcd m n) + (if (= n 0) + m + (gcd n (mod m n)))) +(gcd 18 45) +""" +def test_gcd(): + got = run(gcd_src) + assert got == 9 + + +quicksort_src = """ +(define (quicksort lst) + (if (null? lst) + lst + (begin + (define pivot (car lst)) + (define rest (cdr lst)) + (append + (quicksort + (filter (lambda (x) (< x pivot)) rest)) + (list pivot) + (quicksort + (filter (lambda (x) (>= x pivot)) rest))) + ) + ) +) +(quicksort (list 2 1 6 3 4 0 8 9 7 5)) +""" +def test_quicksort(): + got = run(quicksort_src) + assert got == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + +# Example from Structure and Interpretation of Computer Programs +# https://mitpress.mit.edu/sites/default/files/sicp/full-text/sicp/book/node12.html + +newton_src = """ +(define (sqrt x) + (sqrt-iter 1.0 x)) +(define (sqrt-iter guess x) + (if (good-enough? guess x) + guess + (sqrt-iter (improve guess x) x))) +(define (good-enough? guess x) + (< (abs (- (* guess guess) x)) 0.001)) +(define (improve guess x) + (average guess (/ x guess))) +(define (average x y) + (/ (+ x y) 2)) +(sqrt 123454321) +""" +def test_newton(): + got = run(newton_src) + assert math.isclose(got, 11111) + + +closure_src = """ +(define (make-adder increment) + (lambda (x) (+ increment x)) +) +(define inc (make-adder 1)) +(inc 99) +""" +def test_newton(): + got = run(closure_src) + assert got == 100 diff --git a/18-context-mngr/lispy/py3.10/lis.py b/02-array-seq/lispy/lis.py similarity index 67% rename from 18-context-mngr/lispy/py3.10/lis.py rename to 02-array-seq/lispy/lis.py index ffd53c0..e37ba23 100644 --- a/18-context-mngr/lispy/py3.10/lis.py +++ b/02-array-seq/lispy/lis.py @@ -1,15 +1,16 @@ -################ Lispy: Scheme Interpreter in Python 3.9 +################ Lispy: Scheme Interpreter in Python 3.10 ## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html ## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) ## by Luciano Ramalho, adding type hints and pattern matching. -################ Imports and Types +################ imports and types import math import operator as op from collections import ChainMap -from collections.abc import MutableMapping +from collections.abc import MutableMapping, Iterator +from itertools import chain from typing import Any, TypeAlias Symbol: TypeAlias = str @@ -23,7 +24,9 @@ class Procedure: "A user-defined Scheme procedure." def __init__(self, parms: list[Symbol], body: Expression, env: Environment): - self.parms, self.body, self.env = parms, body, env + self.parms = parms + self.body = body + self.env = env def __call__(self, *args: Expression) -> Any: local_env = dict(zip(self.parms, args)) @@ -31,26 +34,26 @@ def __call__(self, *args: Expression) -> Any: return evaluate(self.body, env) -################ Global Environment +################ global environment def standard_env() -> Environment: "An environment with some Scheme standard procedures." env: Environment = {} env.update(vars(math)) # sin, cos, sqrt, pi, ... - env.update( - { + env.update({ '+': op.add, '-': op.sub, '*': op.mul, '/': op.truediv, + '//': op.floordiv, '>': op.gt, '<': op.lt, '>=': op.ge, '<=': op.le, '=': op.eq, 'abs': abs, - 'append': op.add, + 'append': lambda *args: list(chain(*args)), 'apply': lambda proc, args: proc(*args), 'begin': lambda *x: x[-1], 'car': lambda x: x[0], @@ -58,6 +61,7 @@ def standard_env() -> Environment: 'cons': lambda x, y: [x] + y, 'eq?': op.is_, 'equal?': op.eq, + 'filter': lambda *args: list(filter(*args)), 'length': len, 'list': lambda *x: list(x), 'list?': lambda x: isinstance(x, list), @@ -70,12 +74,11 @@ def standard_env() -> Environment: 'procedure?': callable, 'round': round, 'symbol?': lambda x: isinstance(x, Symbol), - } - ) + }) return env -################ Parsing: parse, tokenize, and read_from_tokens +################ parse, tokenize, and read_from_tokens def parse(program: str) -> Expression: @@ -94,11 +97,11 @@ def read_from_tokens(tokens: list[str]) -> Expression: raise SyntaxError('unexpected EOF while reading') token = tokens.pop(0) if '(' == token: - L = [] + exp = [] while tokens[0] != ')': - L.append(read_from_tokens(tokens)) - tokens.pop(0) # pop off ')' - return L + exp.append(read_from_tokens(tokens)) + tokens.pop(0) # discard ')' + return exp elif ')' == token: raise SyntaxError('unexpected )') else: @@ -116,7 +119,7 @@ def parse_atom(token: str) -> Atom: return Symbol(token) -################ Interaction: A REPL +################ interaction: a REPL def repl(prompt: str = 'lis.py> ') -> None: @@ -138,29 +141,50 @@ def lispstr(exp: object) -> str: ################ eval - -def evaluate(x: Expression, env: Environment) -> Any: +# tag::EVALUATE[] +def evaluate(exp: Expression, env: Environment) -> Any: "Evaluate an expression in an environment." - match x: - case Symbol(var): # variable reference + match exp: + case int(x) | float(x): + return x + case Symbol(var): return env[var] - case literal if not isinstance(x, list): # constant literal - return literal - case ['quote', exp]: # (quote exp) + case []: + return [] + case ['quote', exp]: return exp - case ['if', test, conseq, alt]: # (if test conseq alt) + case ['if', test, consequence, alternative]: if evaluate(test, env): - exp = conseq + return evaluate(consequence, env) else: - exp = alt - return evaluate(exp, env) - case ['lambda', parms, body]: # (lambda (parm...) body) - return Procedure(parms, body, env) - case ['define', Symbol(var), exp]: # (define var exp) - env[var] = evaluate(exp, env) - case ['define', [name, *parms], body]: # (define (fun parm...) body) + return evaluate(alternative, env) + case ['define', Symbol(var), value_exp]: + env[var] = evaluate(value_exp, env) + case ['define', [Symbol(name), *parms], body]: env[name] = Procedure(parms, body, env) - case [op, *args]: # (proc arg...) + case ['lambda', [*parms], body]: + return Procedure(parms, body, env) + case [op, *args]: proc = evaluate(op, env) - values = (evaluate(arg, env) for arg in args) + values = [evaluate(arg, env) for arg in args] return proc(*values) + case _: + raise SyntaxError(repr(exp)) +# end::EVALUATE[] + + +################ non-interactive execution + + +def run_lines(source: str) -> Iterator[Any]: + global_env: Environment = standard_env() + tokens = tokenize(source) + while tokens: + exp = read_from_tokens(tokens) + yield evaluate(exp, global_env) + + +def run(source: str) -> Any: + for result in run_lines(source): + pass + return result diff --git a/18-context-mngr/lispy/py3.10/lis_test.py b/02-array-seq/lispy/lis_test.py similarity index 90% rename from 18-context-mngr/lispy/py3.10/lis_test.py rename to 02-array-seq/lispy/lis_test.py index b2eb28a..e220e74 100644 --- a/18-context-mngr/lispy/py3.10/lis_test.py +++ b/02-array-seq/lispy/lis_test.py @@ -4,6 +4,23 @@ from lis import parse, evaluate, Expression, Environment, standard_env +############################################################# tests for parse + +@mark.parametrize( 'source, expected', [ + ('7', 7), + ('x', 'x'), + ('(sum 1 2 3)', ['sum', 1, 2, 3]), + ('(+ (* 2 100) (* 1 10))', ['+', ['*', 2, 100], ['*', 1, 10]]), + ('99 100', 99), # parse stops at the first complete expression + ('(a)(b)', ['a']), +]) +def test_parse(source: str, expected: Expression) -> None: + got = parse(source) + assert got == expected + + +########################################################## tests for evaluate + # Norvig's tests are not isolated: they assume the # same environment from first to last test. global_env_for_first_test = standard_env() diff --git a/02-array-seq/lispy/meta_test.py b/02-array-seq/lispy/meta_test.py new file mode 100644 index 0000000..cb3d062 --- /dev/null +++ b/02-array-seq/lispy/meta_test.py @@ -0,0 +1,64 @@ +import operator as op + +from lis import run + +env_scm = """ +(define standard-env (list + (list (quote +) +) + (list (quote -) -) +)) +standard-env +""" + +def test_env_build(): + got = run(env_scm) + assert got == [['+', op.add], ['-', op.sub]] + +scan_scm = """ +(define l (quote (a b c))) +(define (scan what where) + (if (null? where) + () + (if (eq? what (car where)) + what + (scan what (cdr where)))) +) +""" + +def test_scan(): + source = scan_scm + '(scan (quote a) l )' + got = run(source) + assert got == 'a' + + +def test_scan_not_found(): + source = scan_scm + '(scan (quote z) l )' + got = run(source) + assert got == [] + + +lookup_scm = """ +(define env (list + (list (quote +) +) + (list (quote -) -) +)) +(define (lookup what where) + (if (null? where) + () + (if (eq? what (car (car where))) + (car (cdr (car where))) + (lookup what (cdr where)))) +) +""" + +def test_lookup(): + source = lookup_scm + '(lookup (quote +) env)' + got = run(source) + assert got == op.add + + +def test_lookup_not_found(): + source = lookup_scm + '(lookup (quote z) env )' + got = run(source) + assert got == [] + diff --git a/02-array-seq/match_lat_lon.py b/02-array-seq/match_lat_lon.py new file mode 100644 index 0000000..592aa70 --- /dev/null +++ b/02-array-seq/match_lat_lon.py @@ -0,0 +1,32 @@ +""" +metro_lat_long.py + +Demonstration of nested tuple unpacking:: + + >>> main() + | latitude | longitude + Mexico City | 19.4333 | -99.1333 + New York-Newark | 40.8086 | -74.0204 + Sao Paulo | -23.5478 | -46.6358 + +""" + +# tag::MAIN[] +metro_areas = [ + ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)), + ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)), + ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)), + ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)), + ('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)), +] + +def main(): + print(f'{"":15} | {"latitude":>9} | {"longitude":>9}') + for record in metro_areas: + match record: + case [name, _, _, (lat, lon)] if lon <= 0: + print(f'{name:15} | {lat:9.4f} | {lon:9.4f}') +# end::MAIN[] + +if __name__ == '__main__': + main() diff --git a/02-array-seq/metro_lat_long.py b/02-array-seq/metro_lat_lon.py similarity index 61% rename from 02-array-seq/metro_lat_long.py rename to 02-array-seq/metro_lat_lon.py index 25b2373..930a993 100644 --- a/02-array-seq/metro_lat_long.py +++ b/02-array-seq/metro_lat_lon.py @@ -4,7 +4,7 @@ Demonstration of nested tuple unpacking:: >>> main() - | lat. | long. + | latitude | longitude Mexico City | 19.4333 | -99.1333 New York-Newark | 40.8086 | -74.0204 Sao Paulo | -23.5478 | -46.6358 @@ -12,7 +12,7 @@ """ metro_areas = [ - ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)), # <1> + ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)), # <1> ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)), ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)), ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)), @@ -20,10 +20,10 @@ ] def main(): - print(f'{"":15} | {"lat.":^9} | {"long.":^9}') - for name, cc, pop, (latitude, longitude) in metro_areas: # <2> - if longitude <= 0: # <3> - print(f'{name:15} | {latitude:9.4f} | {longitude:9.4f}') + print(f'{"":15} | {"latitude":>9} | {"longitude":>9}') + for name, _, _, (lat, lon) in metro_areas: # <2> + if lon <= 0: # <3> + print(f'{name:15} | {lat:9.4f} | {lon:9.4f}') if __name__ == '__main__': main() diff --git a/04-text-byte/simplify.py b/04-text-byte/simplify.py index ebbdcf8..0852bd2 100644 --- a/04-text-byte/simplify.py +++ b/04-text-byte/simplify.py @@ -51,7 +51,7 @@ def shave_marks_latin(txt): if unicodedata.combining(c) and latin_base: # <2> continue # ignore diacritic on Latin base char preserve.append(c) # <3> - # if it isn't combining char, it's a new base char + # if it isn't a combining char, it's a new base char if not unicodedata.combining(c): # <4> latin_base = c in string.ascii_letters shaved = ''.join(preserve) diff --git a/04-text-byte/zwj_sample.py b/04-text-byte/zwj_sample.py index 28893cc..a80f024 100644 --- a/04-text-byte/zwj_sample.py +++ b/04-text-byte/zwj_sample.py @@ -18,8 +18,7 @@ code, descr, version = (s.strip() for s in line.split('|')) chars = [chr(int(c, 16)) for c in code.split()] print(''.join(chars), version, descr, sep='\t', end='') - while chars: - char = chars.pop(0) + for char in chars: if char in markers: print(' + ' + markers[char], end='') else: diff --git a/05-record-like/class/coordinates.py b/05-record-like/class/coordinates.py index 9186c1b..683a97a 100644 --- a/05-record-like/class/coordinates.py +++ b/05-record-like/class/coordinates.py @@ -9,8 +9,8 @@ # tag::COORDINATE[] class Coordinate: - def __init__(self, lat, long): + def __init__(self, lat, lon): self.lat = lat - self.long = long + self.lon = lon -# end::COORDINATE[] \ No newline at end of file +# end::COORDINATE[] diff --git a/05-record-like/dataclass/coordinates.py b/05-record-like/dataclass/coordinates.py index baacb6e..d7ba76b 100644 --- a/05-record-like/dataclass/coordinates.py +++ b/05-record-like/dataclass/coordinates.py @@ -14,10 +14,10 @@ @dataclass(frozen=True) class Coordinate: lat: float - long: float + lon: float def __str__(self): ns = 'N' if self.lat >= 0 else 'S' - we = 'E' if self.long >= 0 else 'W' - return f'{abs(self.lat):.1f}°{ns}, {abs(self.long):.1f}°{we}' + we = 'E' if self.lon >= 0 else 'W' + return f'{abs(self.lat):.1f}°{ns}, {abs(self.lon):.1f}°{we}' # end::COORDINATE[] diff --git a/05-record-like/typing_namedtuple/coordinates.py b/05-record-like/typing_namedtuple/coordinates.py index 382d804..378a430 100644 --- a/05-record-like/typing_namedtuple/coordinates.py +++ b/05-record-like/typing_namedtuple/coordinates.py @@ -13,10 +13,10 @@ class Coordinate(NamedTuple): lat: float - long: float + lon: float def __str__(self): ns = 'N' if self.lat >= 0 else 'S' - we = 'E' if self.long >= 0 else 'W' - return f'{abs(self.lat):.1f}°{ns}, {abs(self.long):.1f}°{we}' -# end::COORDINATE[] \ No newline at end of file + we = 'E' if self.lon >= 0 else 'W' + return f'{abs(self.lat):.1f}°{ns}, {abs(self.lon):.1f}°{we}' +# end::COORDINATE[] diff --git a/05-record-like/typing_namedtuple/coordinates2.py b/05-record-like/typing_namedtuple/coordinates2.py index a251429..e523a11 100644 --- a/05-record-like/typing_namedtuple/coordinates2.py +++ b/05-record-like/typing_namedtuple/coordinates2.py @@ -5,7 +5,7 @@ >>> moscow = Coordinate(55.756, 37.617) >>> moscow - Coordinate(lat=55.756, long=37.617, reference='WGS84') + Coordinate(lat=55.756, lon=37.617, reference='WGS84') """ @@ -15,6 +15,6 @@ class Coordinate(NamedTuple): lat: float # <1> - long: float + lon: float reference: str = 'WGS84' # <2> -# end::COORDINATE[] \ No newline at end of file +# end::COORDINATE[] diff --git a/05-record-like/typing_namedtuple/nocheck_demo.py b/05-record-like/typing_namedtuple/nocheck_demo.py index e1193ec..57e8fc8 100644 --- a/05-record-like/typing_namedtuple/nocheck_demo.py +++ b/05-record-like/typing_namedtuple/nocheck_demo.py @@ -3,7 +3,7 @@ class Coordinate(typing.NamedTuple): lat: float - long: float + lon: float trash = Coordinate('foo', None) # <1> print(trash) diff --git a/09-closure-deco/clockdeco.py b/09-closure-deco/clockdeco.py index e45470d..746711b 100644 --- a/09-closure-deco/clockdeco.py +++ b/09-closure-deco/clockdeco.py @@ -9,12 +9,8 @@ def clocked(*args, **kwargs): result = func(*args, **kwargs) elapsed = time.perf_counter() - t0 name = func.__name__ - arg_lst = [] - if args: - arg_lst.append(', '.join(repr(arg) for arg in args)) - if kwargs: - pairs = [f'{k}={v!r}' for k, v in kwargs.items()] - arg_lst.append(', '.join(pairs)) + arg_lst = [repr(arg) for arg in args] + arg_lst.extend(f'{k}={v!r}' for k, v in kwargs.items()) arg_str = ', '.join(arg_lst) print(f'[{elapsed:0.8f}s] {name}({arg_str}) -> {result!r}') return result diff --git a/09-closure-deco/fibo_demo_cache.py b/09-closure-deco/fibo_demo_cache.py index 5e92ec6..2bd06e7 100644 --- a/09-closure-deco/fibo_demo_cache.py +++ b/09-closure-deco/fibo_demo_cache.py @@ -8,7 +8,7 @@ def fibonacci(n): if n < 2: return n - return fibonacci(n-2) + fibonacci(n-1) + return fibonacci(n - 2) + fibonacci(n - 1) if __name__ == '__main__': diff --git a/09-closure-deco/htmlizer.py b/09-closure-deco/htmlizer.py index cfd92b6..c660ad4 100644 --- a/09-closure-deco/htmlizer.py +++ b/09-closure-deco/htmlizer.py @@ -8,7 +8,7 @@ >>> htmlize(abs) '
<built-in function abs>
' >>> htmlize('Heimlich & Co.\n- a game') # <2> -'

Heimlich & Co.
\n- a game

' +'

Heimlich & Co.
\n- a game

' >>> htmlize(42) # <3> '
42 (0x2a)
' >>> print(htmlize(['alpha', 66, {3, 2, 1}])) # <4> @@ -45,7 +45,7 @@ def htmlize(obj: object) -> str: @htmlize.register # <2> def _(text: str) -> str: # <3> - content = html.escape(text).replace('\n', '
\n') + content = html.escape(text).replace('\n', '
\n') return f'

{content}

' @htmlize.register # <4> diff --git a/11-pythonic-obj/private/Confidential.java b/11-pythonic-obj/private/Confidential.java index 6dc740d..c3887f4 100644 --- a/11-pythonic-obj/private/Confidential.java +++ b/11-pythonic-obj/private/Confidential.java @@ -3,6 +3,6 @@ public class Confidential { private String secret = ""; public Confidential(String text) { - secret = text.toUpperCase(); + this.secret = text.toUpperCase(); } } diff --git a/18-context-mngr/lispy/LICENSE b/18-context-mngr/lispy/LICENSE deleted file mode 100644 index ca550a2..0000000 --- a/18-context-mngr/lispy/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2010-2017 Peter Norvig - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/18-context-mngr/lispy/README.md b/18-context-mngr/lispy/README.md deleted file mode 100644 index cf18e3b..0000000 --- a/18-context-mngr/lispy/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# lis.py - -This directory contains 3 versions of -[Peter Norvig's `lis.py` interpreter](https://norvig.com/lispy.html) -for a subset of [Scheme](https://en.wikipedia.org/wiki/Scheme_(programming_language)). - -* `original/`: Norvig's `lis.py` unchanged, `lispy.py` with -[minor changes](https://github.com/norvig/pytudes/pull/106) to run on Python 3, -and the `lispytest.py` custom test script; -* `py3.9/`: `lis.py` with type hints and a few minor edits—requires Python 3.9; -* `py3.10/`: `lis.py` with type hints, pattern matching, and minor edits—requires Python 3.10. - -The `py3.9/` and `py3.10/` directories also have identical `lis_test.py` to run with -[pytest](https://docs.pytest.org). -These files include all the -[`lis_tests` suite](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/py/lispytest.py#L5) -from `original/lispytest.py`, -and additional separate tests for each expression and special form handled by `evaluate`. - -## Provenance, Copyright and License - -`lis.py` is published in the [norvig/pytudes](https://github.com/norvig/pytudes) repository on Github. -The copyright holder is Peter Norvig and the code is licensed under the -[MIT license](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/LICENSE). - -I wrote the changes and additions described above. - -— Luciano Ramalho ([@ramalho](https://github.com/ramalho/)), 2021-06-09. diff --git a/18-context-mngr/lispy/original/lis.py b/18-context-mngr/lispy/original/lis.py deleted file mode 100644 index f81376a..0000000 --- a/18-context-mngr/lispy/original/lis.py +++ /dev/null @@ -1,132 +0,0 @@ -################ Lispy: Scheme Interpreter in Python 3.3+ - -## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html - -################ Imports and Types - -import math -import operator as op -from collections import ChainMap as Environment - -Symbol = str # A Lisp Symbol is implemented as a Python str -List = list # A Lisp List is implemented as a Python list -Number = (int, float) # A Lisp Number is implemented as a Python int or float - -class Procedure(object): - "A user-defined Scheme procedure." - def __init__(self, parms, body, env): - self.parms, self.body, self.env = parms, body, env - def __call__(self, *args): - env = Environment(dict(zip(self.parms, args)), self.env) - return eval(self.body, env) - -################ Global Environment - -def standard_env(): - "An environment with some Scheme standard procedures." - env = {} - env.update(vars(math)) # sin, cos, sqrt, pi, ... - env.update({ - '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, - '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, - 'abs': abs, - 'append': op.add, - 'apply': lambda proc, args: proc(*args), - 'begin': lambda *x: x[-1], - 'car': lambda x: x[0], - 'cdr': lambda x: x[1:], - 'cons': lambda x,y: [x] + y, - 'eq?': op.is_, - 'equal?': op.eq, - 'length': len, - 'list': lambda *x: list(x), - 'list?': lambda x: isinstance(x,list), - 'map': lambda *args: list(map(*args)), - 'max': max, - 'min': min, - 'not': op.not_, - 'null?': lambda x: x == [], - 'number?': lambda x: isinstance(x, Number), - 'procedure?': callable, - 'round': round, - 'symbol?': lambda x: isinstance(x, Symbol), - }) - return env - -global_env = standard_env() - -################ Parsing: parse, tokenize, and read_from_tokens - -def parse(program): - "Read a Scheme expression from a string." - return read_from_tokens(tokenize(program)) - -def tokenize(s): - "Convert a string into a list of tokens." - return s.replace('(',' ( ').replace(')',' ) ').split() - -def read_from_tokens(tokens): - "Read an expression from a sequence of tokens." - if len(tokens) == 0: - raise SyntaxError('unexpected EOF while reading') - token = tokens.pop(0) - if '(' == token: - L = [] - while tokens[0] != ')': - L.append(read_from_tokens(tokens)) - tokens.pop(0) # pop off ')' - return L - elif ')' == token: - raise SyntaxError('unexpected )') - else: - return atom(token) - -def atom(token): - "Numbers become numbers; every other token is a symbol." - try: return int(token) - except ValueError: - try: return float(token) - except ValueError: - return Symbol(token) - -################ Interaction: A REPL - -def repl(prompt='lis.py> '): - "A prompt-read-eval-print loop." - while True: - val = eval(parse(input(prompt))) - if val is not None: - print(lispstr(val)) - -def lispstr(exp): - "Convert a Python object back into a Lisp-readable string." - if isinstance(exp, List): - return '(' + ' '.join(map(lispstr, exp)) + ')' - else: - return str(exp) - -################ eval - -def eval(x, env=global_env): - "Evaluate an expression in an environment." - if isinstance(x, Symbol): # variable reference - return env[x] - elif not isinstance(x, List): # constant literal - return x - elif x[0] == 'quote': # (quote exp) - (_, exp) = x - return exp - elif x[0] == 'if': # (if test conseq alt) - (_, test, conseq, alt) = x - exp = (conseq if eval(test, env) else alt) - return eval(exp, env) - elif x[0] == 'define': # (define var exp) - (_, var, exp) = x - env[var] = eval(exp, env) - elif x[0] == 'lambda': # (lambda (var...) body) - (_, parms, body) = x - return Procedure(parms, body, env) - else: # (proc arg...) - proc = eval(x[0], env) - args = [eval(exp, env) for exp in x[1:]] - return proc(*args) diff --git a/18-context-mngr/lispy/original/lispy.py b/18-context-mngr/lispy/original/lispy.py deleted file mode 100644 index b17341c..0000000 --- a/18-context-mngr/lispy/original/lispy.py +++ /dev/null @@ -1,316 +0,0 @@ -################ Scheme Interpreter in Python - -## (c) Peter Norvig, 2010; See http://norvig.com/lispy2.html - -################ Symbol, Procedure, classes - -import re, sys, io - -class Symbol(str): pass - -def Sym(s, symbol_table={}): - "Find or create unique Symbol entry for str s in symbol table." - if s not in symbol_table: symbol_table[s] = Symbol(s) - return symbol_table[s] - -_quote, _if, _set, _define, _lambda, _begin, _definemacro, = map(Sym, -"quote if set! define lambda begin define-macro".split()) - -_quasiquote, _unquote, _unquotesplicing = map(Sym, -"quasiquote unquote unquote-splicing".split()) - -class Procedure: - "A user-defined Scheme procedure." - def __init__(self, parms, exp, env): - self.parms, self.exp, self.env = parms, exp, env - def __call__(self, *args): - return eval(self.exp, Env(self.parms, args, self.env)) - -################ parse, read, and user interaction - -def parse(inport): - "Parse a program: read and expand/error-check it." - # Backwards compatibility: given a str, convert it to an InPort - if isinstance(inport, str): inport = InPort(io.StringIO(inport)) - return expand(read(inport), toplevel=True) - -eof_object = Symbol('#') # Note: uninterned; can't be read - -class InPort: - "An input port. Retains a line of chars." - tokenizer = r"""\s*(,@|[('`,)]|"(?:[\\].|[^\\"])*"|;.*|[^\s('"`,;)]*)(.*)""" - def __init__(self, file): - self.file = file; self.line = '' - def next_token(self): - "Return the next token, reading new text into line buffer if needed." - while True: - if self.line == '': self.line = self.file.readline() - if self.line == '': return eof_object - token, self.line = re.match(InPort.tokenizer, self.line).groups() - if token != '' and not token.startswith(';'): - return token - -def readchar(inport): - "Read the next character from an input port." - if inport.line != '': - ch, inport.line = inport.line[0], inport.line[1:] - return ch - else: - return inport.file.read(1) or eof_object - -def read(inport): - "Read a Scheme expression from an input port." - def read_ahead(token): - if '(' == token: - L = [] - while True: - token = inport.next_token() - if token == ')': return L - else: L.append(read_ahead(token)) - elif ')' == token: raise SyntaxError('unexpected )') - elif token in quotes: return [quotes[token], read(inport)] - elif token is eof_object: raise SyntaxError('unexpected EOF in list') - else: return atom(token) - # body of read: - token1 = inport.next_token() - return eof_object if token1 is eof_object else read_ahead(token1) - -quotes = {"'":_quote, "`":_quasiquote, ",":_unquote, ",@":_unquotesplicing} - -def atom(token): - 'Numbers become numbers; #t and #f are booleans; "..." string; otherwise Symbol.' - if token == '#t': return True - elif token == '#f': return False - elif token[0] == '"': return token[1:-1] - try: return int(token) - except ValueError: - try: return float(token) - except ValueError: - try: return complex(token.replace('i', 'j', 1)) - except ValueError: - return Sym(token) - -def to_string(x): - "Convert a Python object back into a Lisp-readable string." - if x is True: return "#t" - elif x is False: return "#f" - elif isa(x, Symbol): return x - elif isa(x, str): return repr(x) - elif isa(x, list): return '('+' '.join(map(to_string, x))+')' - elif isa(x, complex): return str(x).replace('j', 'i') - else: return str(x) - -def load(filename): - "Eval every expression from a file." - repl(None, InPort(open(filename)), None) - -def repl(prompt='lispy> ', inport=InPort(sys.stdin), out=sys.stdout): - "A prompt-read-eval-print loop." - sys.stderr.write("Lispy version 2.0\n") - while True: - try: - if prompt: sys.stderr.write(prompt) - x = parse(inport) - if x is eof_object: return - val = eval(x) - if val is not None and out: print(to_string(val), file=out) - except Exception as e: - print('%s: %s' % (type(e).__name__, e)) - -################ Environment class - -class Env(dict): - "An environment: a dict of {'var':val} pairs, with an outer Env." - def __init__(self, parms=(), args=(), outer=None): - # Bind parm list to corresponding args, or single parm to list of args - self.outer = outer - if isa(parms, Symbol): - self.update({parms:list(args)}) - else: - if len(args) != len(parms): - raise TypeError('expected %s, given %s, ' - % (to_string(parms), to_string(args))) - self.update(zip(parms,args)) - def find(self, var): - "Find the innermost Env where var appears." - if var in self: return self - elif self.outer is None: raise LookupError(var) - else: return self.outer.find(var) - -def is_pair(x): return x != [] and isa(x, list) -def cons(x, y): return [x]+y - -def callcc(proc): - "Call proc with current continuation; escape only" - ball = RuntimeWarning("Sorry, can't continue this continuation any longer.") - def throw(retval): ball.retval = retval; raise ball - try: - return proc(throw) - except RuntimeWarning as w: - if w is ball: return ball.retval - else: raise w - -def add_globals(self): - "Add some Scheme standard procedures." - import math, cmath, operator as op - self.update(vars(math)) - self.update(vars(cmath)) - self.update({ - '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, 'not':op.not_, - '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, - 'equal?':op.eq, 'eq?':op.is_, 'length':len, 'cons':cons, - 'car':lambda x:x[0], 'cdr':lambda x:x[1:], 'append':op.add, - 'list':lambda *x:list(x), 'list?': lambda x:isa(x,list), - 'null?':lambda x:x==[], 'symbol?':lambda x: isa(x, Symbol), - 'boolean?':lambda x: isa(x, bool), 'pair?':is_pair, - 'port?': lambda x:isa(x,file), 'apply':lambda proc,l: proc(*l), - 'eval':lambda x: eval(expand(x)), 'load':lambda fn: load(fn), 'call/cc':callcc, - 'open-input-file':open,'close-input-port':lambda p: p.file.close(), - 'open-output-file':lambda f:open(f,'w'), 'close-output-port':lambda p: p.close(), - 'eof-object?':lambda x:x is eof_object, 'read-char':readchar, - 'read':read, 'write':lambda x,port=sys.stdout:port.write(to_string(x)), - 'display':lambda x,port=sys.stdout:port.write(x if isa(x,str) else to_string(x))}) - return self - -isa = isinstance - -global_env = add_globals(Env()) - -################ eval (tail recursive) - -def eval(x, env=global_env): - "Evaluate an expression in an environment." - while True: - if isa(x, Symbol): # variable reference - return env.find(x)[x] - elif not isa(x, list): # constant literal - return x - elif x[0] is _quote: # (quote exp) - (_, exp) = x - return exp - elif x[0] is _if: # (if test conseq alt) - (_, test, conseq, alt) = x - x = (conseq if eval(test, env) else alt) - elif x[0] is _set: # (set! var exp) - (_, var, exp) = x - env.find(var)[var] = eval(exp, env) - return None - elif x[0] is _define: # (define var exp) - (_, var, exp) = x - env[var] = eval(exp, env) - return None - elif x[0] is _lambda: # (lambda (var*) exp) - (_, vars, exp) = x - return Procedure(vars, exp, env) - elif x[0] is _begin: # (begin exp+) - for exp in x[1:-1]: - eval(exp, env) - x = x[-1] - else: # (proc exp*) - exps = [eval(exp, env) for exp in x] - proc = exps.pop(0) - if isa(proc, Procedure): - x = proc.exp - env = Env(proc.parms, exps, proc.env) - else: - return proc(*exps) - -################ expand - -def expand(x, toplevel=False): - "Walk tree of x, making optimizations/fixes, and signaling SyntaxError." - require(x, x!=[]) # () => Error - if not isa(x, list): # constant => unchanged - return x - elif x[0] is _quote: # (quote exp) - require(x, len(x)==2) - return x - elif x[0] is _if: - if len(x)==3: x = x + [None] # (if t c) => (if t c None) - require(x, len(x)==4) - return list(map(expand, x)) - elif x[0] is _set: - require(x, len(x)==3); - var = x[1] # (set! non-var exp) => Error - require(x, isa(var, Symbol), "can set! only a symbol") - return [_set, var, expand(x[2])] - elif x[0] is _define or x[0] is _definemacro: - require(x, len(x)>=3) - _def, v, body = x[0], x[1], x[2:] - if isa(v, list) and v: # (define (f args) body) - f, args = v[0], v[1:] # => (define f (lambda (args) body)) - return expand([_def, f, [_lambda, args]+body]) - else: - require(x, len(x)==3) # (define non-var/list exp) => Error - require(x, isa(v, Symbol), "can define only a symbol") - exp = expand(x[2]) - if _def is _definemacro: - require(x, toplevel, "define-macro only allowed at top level") - proc = eval(exp) - require(x, callable(proc), "macro must be a procedure") - macro_table[v] = proc # (define-macro v proc) - return None # => None; add v:proc to macro_table - return [_define, v, exp] - elif x[0] is _begin: - if len(x)==1: return None # (begin) => None - else: return [expand(xi, toplevel) for xi in x] - elif x[0] is _lambda: # (lambda (x) e1 e2) - require(x, len(x)>=3) # => (lambda (x) (begin e1 e2)) - vars, body = x[1], x[2:] - require(x, (isa(vars, list) and all(isa(v, Symbol) for v in vars)) - or isa(vars, Symbol), "illegal lambda argument list") - exp = body[0] if len(body) == 1 else [_begin] + body - return [_lambda, vars, expand(exp)] - elif x[0] is _quasiquote: # `x => expand_quasiquote(x) - require(x, len(x)==2) - return expand_quasiquote(x[1]) - elif isa(x[0], Symbol) and x[0] in macro_table: - return expand(macro_table[x[0]](*x[1:]), toplevel) # (m arg...) - else: # => macroexpand if m isa macro - return list(map(expand, x)) # (f arg...) => expand each - -def require(x, predicate, msg="wrong length"): - "Signal a syntax error if predicate is false." - if not predicate: raise SyntaxError(to_string(x)+': '+msg) - -_append, _cons, _let = map(Sym, "append cons let".split()) - -def expand_quasiquote(x): - """Expand `x => 'x; `,x => x; `(,@x y) => (append x y) """ - if not is_pair(x): - return [_quote, x] - require(x, x[0] is not _unquotesplicing, "can't splice here") - if x[0] is _unquote: - require(x, len(x)==2) - return x[1] - elif is_pair(x[0]) and x[0][0] is _unquotesplicing: - require(x[0], len(x[0])==2) - return [_append, x[0][1], expand_quasiquote(x[1:])] - else: - return [_cons, expand_quasiquote(x[0]), expand_quasiquote(x[1:])] - -def let(*args): - args = list(args) - x = cons(_let, args) - require(x, len(args)>1) - bindings, body = args[0], args[1:] - require(x, all(isa(b, list) and len(b)==2 and isa(b[0], Symbol) - for b in bindings), "illegal binding list") - vars, vals = zip(*bindings) - return [[_lambda, list(vars)]+list(map(expand, body))] + list(map(expand, vals)) - -macro_table = {_let:let} ## More macros can go here - -eval(parse("""(begin - -(define-macro and (lambda args - (if (null? args) #t - (if (= (length args) 1) (car args) - `(if ,(car args) (and ,@(cdr args)) #f))))) - -;; More macros can also go here - -)""")) - -if __name__ == '__main__': - repl() diff --git a/18-context-mngr/lispy/original/lispytest.py b/18-context-mngr/lispy/original/lispytest.py deleted file mode 100644 index b324ff3..0000000 --- a/18-context-mngr/lispy/original/lispytest.py +++ /dev/null @@ -1,122 +0,0 @@ -from __future__ import print_function - -################ Tests for lis.py and lispy.py - -lis_tests = [ - ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), - ("(+ 2 2)", 4), - ("(+ (* 2 100) (* 1 10))", 210), - ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), - ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), - ("(define x 3)", None), ("x", 3), ("(+ x x)", 6), - ("((lambda (x) (+ x x)) 5)", 10), - ("(define twice (lambda (x) (* 2 x)))", None), ("(twice 5)", 10), - ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), - ("((compose list twice) 5)", [10]), - ("(define repeat (lambda (f) (compose f f)))", None), - ("((repeat twice) 5)", 20), ("((repeat (repeat twice)) 5)", 80), - ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), - ("(fact 3)", 6), - ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), - ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), - ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), - ("""(define combine (lambda (f) - (lambda (x y) - (if (null? x) (quote ()) - (f (list (car x) (car y)) - ((combine f) (cdr x) (cdr y)))))))""", None), - ("(define zip (combine cons))", None), - ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), - ("""(define riff-shuffle (lambda (deck) (begin - (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) - (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) - (define mid (lambda (seq) (/ (length seq) 2))) - ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), - ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), - ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), - ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), - ] - -lispy_tests = [ - ("()", SyntaxError), ("(set! x)", SyntaxError), - ("(define 3 4)", SyntaxError), - ("(quote 1 2)", SyntaxError), ("(if 1 2 3 4)", SyntaxError), - ("(lambda 3 3)", SyntaxError), ("(lambda (x))", SyntaxError), - ("""(if (= 1 2) (define-macro a 'a) - (define-macro a 'b))""", SyntaxError), - ("(define (twice x) (* 2 x))", None), ("(twice 2)", 4), - ("(twice 2 2)", TypeError), - ("(define lyst (lambda items items))", None), - ("(lyst 1 2 3 (+ 2 2))", [1,2,3,4]), - ("(if 1 2)", 2), - ("(if (= 3 4) 2)", None), - ("(begin (define x 1) (set! x (+ x 1)) (+ x 1))", 3), - ("(define ((account bal) amt) (set! bal (+ bal amt)) bal)", None), - ("(define a1 (account 100))", None), - ("(a1 0)", 100), ("(a1 10)", 110), ("(a1 10)", 120), - ("""(define (newton guess function derivative epsilon) - (define guess2 (- guess (/ (function guess) (derivative guess)))) - (if (< (abs (- guess guess2)) epsilon) guess2 - (newton guess2 function derivative epsilon)))""", None), - ("""(define (square-root a) - (newton 1 (lambda (x) (- (* x x) a)) (lambda (x) (* 2 x)) 1e-8))""", None), - ("(> (square-root 200.) 14.14213)", True), - ("(< (square-root 200.) 14.14215)", True), - ("(= (square-root 200.) (sqrt 200.))", True), - ("""(define (sum-squares-range start end) - (define (sumsq-acc start end acc) - (if (> start end) acc (sumsq-acc (+ start 1) end (+ (* start start) acc)))) - (sumsq-acc start end 0))""", None), - ("(sum-squares-range 1 3000)", 9004500500), ## Tests tail recursion - ("(call/cc (lambda (throw) (+ 5 (* 10 (throw 1))))) ;; throw", 1), - ("(call/cc (lambda (throw) (+ 5 (* 10 1)))) ;; do not throw", 15), - ("""(call/cc (lambda (throw) - (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (escape 3)))))))) ; 1 level""", 35), - ("""(call/cc (lambda (throw) - (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (throw 3)))))))) ; 2 levels""", 3), - ("""(call/cc (lambda (throw) - (+ 5 (* 10 (call/cc (lambda (escape) (* 100 1))))))) ; 0 levels""", 1005), - ("(* 1i 1i)", -1), ("(sqrt -1)", 1j), - ("(let ((a 1) (b 2)) (+ a b))", 3), - ("(let ((a 1) (b 2 3)) (+ a b))", SyntaxError), - ("(and 1 2 3)", 3), ("(and (> 2 1) 2 3)", 3), ("(and)", True), - ("(and (> 2 1) (> 2 3))", False), - ("(define-macro unless (lambda args `(if (not ,(car args)) (begin ,@(cdr args))))) ; test `", None), - ("(unless (= 2 (+ 1 1)) (display 2) 3 4)", None), - (r'(unless (= 4 (+ 1 1)) (display 2) (display "\n") 3 4)', 4), - ("(quote x)", 'x'), - ("(quote (1 2 three))", [1, 2, 'three']), - ("'x", 'x'), - ("'(one 2 3)", ['one', 2, 3]), - ("(define L (list 1 2 3))", None), - ("`(testing ,@L testing)", ['testing',1,2,3,'testing']), - ("`(testing ,L testing)", ['testing',[1,2,3],'testing']), - ("`,@L", SyntaxError), - ("""'(1 ;test comments ' - ;skip this line - 2 ; more ; comments ; ) ) - 3) ; final comment""", [1,2,3]), - ] - -def test(tests, name=''): - "For each (exp, expected) test case, see if eval(parse(exp)) == expected." - fails = 0 - for (x, expected) in tests: - try: - result = eval(parse(x)) - print(x, '=>', lispstr(result)) - ok = (result == expected) - except Exception as e: - print(x, '=raises=>', type(e).__name__, e) - ok = isinstance(expected, type) and issubclass(expected, Exception) and isinstance(e, expected) - if not ok: - fails += 1 - print('FAIL!!! Expected', expected) - print('%s %s: %d out of %d tests fail.' % ('*'*45, name, fails, len(tests))) - -if __name__ == '__main__': - from lis import * - test(lis_tests, 'lis.py') - from lispy import * - test(lis_tests+lispy_tests, 'lispy.py') - diff --git a/18-context-mngr/lispy/py3.10/mypy.ini b/18-context-mngr/lispy/py3.10/mypy.ini deleted file mode 100644 index 3456561..0000000 --- a/18-context-mngr/lispy/py3.10/mypy.ini +++ /dev/null @@ -1,2 +0,0 @@ -[mypy] -python_version = 3.10 diff --git a/18-context-mngr/lispy/py3.9/lis.py b/18-context-mngr/lispy/py3.9/lis.py deleted file mode 100644 index 1a14935..0000000 --- a/18-context-mngr/lispy/py3.9/lis.py +++ /dev/null @@ -1,163 +0,0 @@ -################ Lispy: Scheme Interpreter in Python 3.9 - -## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html -## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) -## by Luciano Ramalho, mostly adding type hints. - -################ Imports and Types - -import math -import operator as op -from collections import ChainMap -from collections.abc import MutableMapping -from typing import Union, Any - -Symbol = str -Atom = Union[float, int, Symbol] -Expression = Union[Atom, list] - -Environment = MutableMapping[Symbol, object] - - -class Procedure: - "A user-defined Scheme procedure." - def __init__(self, parms: list[Symbol], body: Expression, env: Environment): - self.parms, self.body, self.env = parms, body, env - - def __call__(self, *args: Expression) -> Any: - local_env = dict(zip(self.parms, args)) - env: Environment = ChainMap(local_env, self.env) - return evaluate(self.body, env) - - -################ Global Environment - - -def standard_env() -> Environment: - "An environment with some Scheme standard procedures." - env: Environment = {} - env.update(vars(math)) # sin, cos, sqrt, pi, ... - env.update( - { - '+': op.add, - '-': op.sub, - '*': op.mul, - '/': op.truediv, - '>': op.gt, - '<': op.lt, - '>=': op.ge, - '<=': op.le, - '=': op.eq, - 'abs': abs, - 'append': op.add, - 'apply': lambda proc, args: proc(*args), - 'begin': lambda *x: x[-1], - 'car': lambda x: x[0], - 'cdr': lambda x: x[1:], - 'cons': lambda x, y: [x] + y, - 'eq?': op.is_, - 'equal?': op.eq, - 'length': len, - 'list': lambda *x: list(x), - 'list?': lambda x: isinstance(x, list), - 'map': lambda *args: list(map(*args)), - 'max': max, - 'min': min, - 'not': op.not_, - 'null?': lambda x: x == [], - 'number?': lambda x: isinstance(x, (int, float)), - 'procedure?': callable, - 'round': round, - 'symbol?': lambda x: isinstance(x, Symbol), - } - ) - return env - - -################ Parsing: parse, tokenize, and read_from_tokens - - -def parse(program: str) -> Expression: - "Read a Scheme expression from a string." - return read_from_tokens(tokenize(program)) - - -def tokenize(s: str) -> list[str]: - "Convert a string into a list of tokens." - return s.replace('(', ' ( ').replace(')', ' ) ').split() - - -def read_from_tokens(tokens: list[str]) -> Expression: - "Read an expression from a sequence of tokens." - if len(tokens) == 0: - raise SyntaxError('unexpected EOF while reading') - token = tokens.pop(0) - if '(' == token: - L = [] - while tokens[0] != ')': - L.append(read_from_tokens(tokens)) - tokens.pop(0) # pop off ')' - return L - elif ')' == token: - raise SyntaxError('unexpected )') - else: - return parse_atom(token) - - -def parse_atom(token: str) -> Atom: - "Numbers become numbers; every other token is a symbol." - try: - return int(token) - except ValueError: - try: - return float(token) - except ValueError: - return Symbol(token) - - -################ Interaction: A REPL - - -def repl(prompt: str = 'lis.py> ') -> None: - "A prompt-read-evaluate-print loop." - global_env: Environment = standard_env() - while True: - val = evaluate(parse(input(prompt)), global_env) - if val is not None: - print(lispstr(val)) - - -def lispstr(exp: object) -> str: - "Convert a Python object back into a Lisp-readable string." - if isinstance(exp, list): - return '(' + ' '.join(map(lispstr, exp)) + ')' - else: - return str(exp) - - -################ eval - - -def evaluate(x: Expression, env: Environment) -> Any: - "Evaluate an expression in an environment." - if isinstance(x, str): # variable reference - return env[x] - elif not isinstance(x, list): # constant literal - return x - elif x[0] == 'quote': # (quote exp) - (_, exp) = x - return exp - elif x[0] == 'if': # (if test conseq alt) - (_, test, conseq, alt) = x - exp = conseq if evaluate(test, env) else alt - return evaluate(exp, env) - elif x[0] == 'define': # (define var exp) - (_, var, exp) = x - env[var] = evaluate(exp, env) - elif x[0] == 'lambda': # (lambda (var...) body) - (_, parms, body) = x - return Procedure(parms, body, env) - else: # (proc arg...) - proc = evaluate(x[0], env) - args = [evaluate(exp, env) for exp in x[1:]] - return proc(*args) diff --git a/18-context-mngr/lispy/py3.9/lis_test.py b/18-context-mngr/lispy/py3.9/lis_test.py deleted file mode 100755 index 44c3402..0000000 --- a/18-context-mngr/lispy/py3.9/lis_test.py +++ /dev/null @@ -1,151 +0,0 @@ -from typing import Optional - -from pytest import mark, fixture - -from lis import parse, evaluate, Expression, Environment, standard_env - -# Norvig's tests are not isolated: they assume the -# same environment from first to last test. -ENV_FOR_FIRST_TEST = standard_env() - -@mark.parametrize( 'source, expected', [ - ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), - ("(+ 2 2)", 4), - ("(+ (* 2 100) (* 1 10))", 210), - ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), - ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), - ("(define x 3)", None), - ("x", 3), - ("(+ x x)", 6), - ("((lambda (x) (+ x x)) 5)", 10), - ("(define twice (lambda (x) (* 2 x)))", None), - ("(twice 5)", 10), - ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), - ("((compose list twice) 5)", [10]), - ("(define repeat (lambda (f) (compose f f)))", None), - ("((repeat twice) 5)", 20), - ("((repeat (repeat twice)) 5)", 80), - ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), - ("(fact 3)", 6), - ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), - ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), - ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), - ("""(define combine (lambda (f) - (lambda (x y) - (if (null? x) (quote ()) - (f (list (car x) (car y)) - ((combine f) (cdr x) (cdr y)))))))""", None), - ("(define zip (combine cons))", None), - ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), - ("""(define riff-shuffle (lambda (deck) - (begin - (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) - (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) - (define mid (lambda (seq) (/ (length seq) 2))) - ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), - ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), - ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), - ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), -]) -def test_evaluate(source: str, expected: Optional[Expression]) -> None: - got = evaluate(parse(source), ENV_FOR_FIRST_TEST) - assert got == expected - - -@fixture -def std_env() -> Environment: - return standard_env() - -# tests for each of the cases in evaluate - -def test_evaluate_variable() -> None: - env: Environment = dict(x=10) - source = 'x' - expected = 10 - got = evaluate(parse(source), env) - assert got == expected - - -def test_evaluate_literal(std_env: Environment) -> None: - source = '3.3' - expected = 3.3 - got = evaluate(parse(source), std_env) - assert got == expected - - -def test_evaluate_quote(std_env: Environment) -> None: - source = '(quote (1.1 is not 1))' - expected = [1.1, 'is', 'not', 1] - got = evaluate(parse(source), std_env) - assert got == expected - - -def test_evaluate_if_true(std_env: Environment) -> None: - source = '(if 1 10 no-such-thing)' - expected = 10 - got = evaluate(parse(source), std_env) - assert got == expected - - -def test_evaluate_if_false(std_env: Environment) -> None: - source = '(if 0 no-such-thing 20)' - expected = 20 - got = evaluate(parse(source), std_env) - assert got == expected - - -def test_define(std_env: Environment) -> None: - source = '(define answer (* 6 7))' - got = evaluate(parse(source), std_env) - assert got is None - assert std_env['answer'] == 42 - - -def test_lambda(std_env: Environment) -> None: - source = '(lambda (a b) (if (>= a b) a b))' - func = evaluate(parse(source), std_env) - assert func.parms == ['a', 'b'] - assert func.body == ['if', ['>=', 'a', 'b'], 'a', 'b'] - assert func.env is std_env - assert func(1, 2) == 2 - assert func(3, 2) == 3 - - -def test_begin(std_env: Environment) -> None: - source = """ - (begin - (define x (* 2 3)) - (* x 7) - ) - """ - got = evaluate(parse(source), std_env) - assert got == 42 - - -def test_invocation_builtin_car(std_env: Environment) -> None: - source = '(car (quote (11 22 33)))' - got = evaluate(parse(source), std_env) - assert got == 11 - - -def test_invocation_builtin_append(std_env: Environment) -> None: - source = '(append (quote (a b)) (quote (c d)))' - got = evaluate(parse(source), std_env) - assert got == ['a', 'b', 'c', 'd'] - - -def test_invocation_builtin_map(std_env: Environment) -> None: - source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))' - got = evaluate(parse(source), std_env) - assert got == [2, 4, 6] - - -def test_invocation_user_procedure(std_env: Environment) -> None: - source = """ - (begin - (define max (lambda (a b) (if (>= a b) a b))) - (max 22 11) - ) - """ - got = evaluate(parse(source), std_env) - assert got == 22 diff --git a/18-context-mngr/lispy/py3.9/mypy.ini b/18-context-mngr/lispy/py3.9/mypy.ini deleted file mode 100644 index 6fcaf90..0000000 --- a/18-context-mngr/lispy/py3.9/mypy.ini +++ /dev/null @@ -1,2 +0,0 @@ -[mypy] -python_version = 3.9 From 1754a511f0333ca7a96eab171348dd1d27f02c44 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Tue, 29 Jun 2021 12:21:27 -0300 Subject: [PATCH 050/127] updated lis.py examples --- 02-array-seq/lispy/LICENSE | 21 ++ 02-array-seq/lispy/README.md | 36 ++ 02-array-seq/lispy/original/lis.py | 135 ++++++++ 02-array-seq/lispy/original/lispy.py | 316 ++++++++++++++++++ 02-array-seq/lispy/original/lispytest.py | 122 +++++++ .../lispy/{ => py3.10}/examples_test.py | 0 02-array-seq/lispy/{ => py3.10}/lis.py | 10 +- 02-array-seq/lispy/{ => py3.10}/lis_test.py | 6 +- 02-array-seq/lispy/{ => py3.10}/meta_test.py | 0 9 files changed, 639 insertions(+), 7 deletions(-) create mode 100644 02-array-seq/lispy/LICENSE create mode 100644 02-array-seq/lispy/README.md create mode 100644 02-array-seq/lispy/original/lis.py create mode 100644 02-array-seq/lispy/original/lispy.py create mode 100644 02-array-seq/lispy/original/lispytest.py rename 02-array-seq/lispy/{ => py3.10}/examples_test.py (100%) rename 02-array-seq/lispy/{ => py3.10}/lis.py (95%) rename 02-array-seq/lispy/{ => py3.10}/lis_test.py (97%) rename 02-array-seq/lispy/{ => py3.10}/meta_test.py (100%) diff --git a/02-array-seq/lispy/LICENSE b/02-array-seq/lispy/LICENSE new file mode 100644 index 0000000..ca550a2 --- /dev/null +++ b/02-array-seq/lispy/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2010-2017 Peter Norvig + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/02-array-seq/lispy/README.md b/02-array-seq/lispy/README.md new file mode 100644 index 0000000..0c11fd8 --- /dev/null +++ b/02-array-seq/lispy/README.md @@ -0,0 +1,36 @@ +# Norvig's originals and updates + +This directory contains: + +* `original/`: +Norvig's [`lis.py`](https://github.com/norvig/pytudes/blob/c33cd6835a506a57d9fe73e3a8317d49babb13e8/py/lis.py), +[`lispy.py`](https://github.com/norvig/pytudes/blob/c33cd6835a506a57d9fe73e3a8317d49babb13e8/py/lispy.py), and the `lispytest.py` custom test script for testing both; +* `py3.10/`: `lis.py` with type hints, pattern matching, and minor edits—requires Python 3.10. + +The `py3.10/` directory also has `lis_test.py` to run with +[pytest](https://docs.pytest.org), including all the +[`lis_tests` suite](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/py/lispytest.py#L5) +from `original/lispytest.py`, +and additional separate tests for each expression and special form handled by `evaluate`. + + +## Provenance, Copyright and License + +`lis.py` is +[published](https://github.com/norvig/pytudes/blob/c33cd6835a506a57d9fe73e3a8317d49babb13e8/py/lis.py) +in the [norvig/pytudes](https://github.com/norvig/pytudes) repository on Github. +The copyright holder is Peter Norvig and the code is licensed under the +[MIT license](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/LICENSE). + + +## Changes to Norvig's code + +I made small changes to the programs in `original/`: + +* In `lis.py`: + * The `Procedure` class accepts a list of expressions as the `body`, and `__call__` evaluates all those expressions in order, returning the value of the last. This is consistent with Scheme's `lambda` syntax and provided a useful example for pattern matching. + * In the `elif` block for `'lambda'`, I added the `*` in front of the `*body` variable in the tuple unpacking to capture the expressions as a list, before calling the `Procedure` constructor. + +* In `lispy.py` I made [changes and a pull request](https://github.com/norvig/pytudes/pull/106) to make it run on Python 3. + +_Luciano Ramalho
June 29, 2021_ \ No newline at end of file diff --git a/02-array-seq/lispy/original/lis.py b/02-array-seq/lispy/original/lis.py new file mode 100644 index 0000000..6e43fa2 --- /dev/null +++ b/02-array-seq/lispy/original/lis.py @@ -0,0 +1,135 @@ +################ Lispy: Scheme Interpreter in Python 3.3+ + +## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html + +################ Imports and Types + +import math +import operator as op +from collections import ChainMap as Environment + +Symbol = str # A Lisp Symbol is implemented as a Python str +List = list # A Lisp List is implemented as a Python list +Number = (int, float) # A Lisp Number is implemented as a Python int or float + +class Procedure(object): + "A user-defined Scheme procedure." + def __init__(self, parms, body, env): + self.parms, self.body, self.env = parms, body, env + def __call__(self, *args): + env = Environment(dict(zip(self.parms, args)), self.env) + for exp in self.body: + result = eval(exp, env) + return result + + +################ Global Environment + +def standard_env(): + "An environment with some Scheme standard procedures." + env = {} + env.update(vars(math)) # sin, cos, sqrt, pi, ... + env.update({ + '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, + '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, + 'abs': abs, + 'append': op.add, + 'apply': lambda proc, args: proc(*args), + 'begin': lambda *x: x[-1], + 'car': lambda x: x[0], + 'cdr': lambda x: x[1:], + 'cons': lambda x,y: [x] + y, + 'eq?': op.is_, + 'equal?': op.eq, + 'length': len, + 'list': lambda *x: list(x), + 'list?': lambda x: isinstance(x,list), + 'map': lambda *args: list(map(*args)), + 'max': max, + 'min': min, + 'not': op.not_, + 'null?': lambda x: x == [], + 'number?': lambda x: isinstance(x, Number), + 'procedure?': callable, + 'round': round, + 'symbol?': lambda x: isinstance(x, Symbol), + }) + return env + +global_env = standard_env() + +################ Parsing: parse, tokenize, and read_from_tokens + +def parse(program): + "Read a Scheme expression from a string." + return read_from_tokens(tokenize(program)) + +def tokenize(s): + "Convert a string into a list of tokens." + return s.replace('(',' ( ').replace(')',' ) ').split() + +def read_from_tokens(tokens): + "Read an expression from a sequence of tokens." + if len(tokens) == 0: + raise SyntaxError('unexpected EOF while reading') + token = tokens.pop(0) + if '(' == token: + L = [] + while tokens[0] != ')': + L.append(read_from_tokens(tokens)) + tokens.pop(0) # pop off ')' + return L + elif ')' == token: + raise SyntaxError('unexpected )') + else: + return atom(token) + +def atom(token): + "Numbers become numbers; every other token is a symbol." + try: return int(token) + except ValueError: + try: return float(token) + except ValueError: + return Symbol(token) + +################ Interaction: A REPL + +def repl(prompt='lis.py> '): + "A prompt-read-eval-print loop." + while True: + val = eval(parse(input(prompt))) + if val is not None: + print(lispstr(val)) + +def lispstr(exp): + "Convert a Python object back into a Lisp-readable string." + if isinstance(exp, List): + return '(' + ' '.join(map(lispstr, exp)) + ')' + else: + return str(exp) + +################ eval + +def eval(x, env=global_env): + "Evaluate an expression in an environment." + if isinstance(x, Symbol): # variable reference + return env[x] + elif not isinstance(x, List): # constant literal + return x + elif x[0] == 'quote': # (quote exp) + (_, exp) = x + return exp + elif x[0] == 'if': # (if test conseq alt) + (_, test, conseq, alt) = x + exp = (conseq if eval(test, env) else alt) + return eval(exp, env) + elif x[0] == 'define': # (define var exp) + (_, var, exp) = x + env[var] = eval(exp, env) + elif x[0] == 'lambda': # (lambda (var...) body) + (_, parms, *body) = x + return Procedure(parms, body, env) + else: # (proc arg...) + proc = eval(x[0], env) + args = [eval(exp, env) for exp in x[1:]] + return proc(*args) diff --git a/02-array-seq/lispy/original/lispy.py b/02-array-seq/lispy/original/lispy.py new file mode 100644 index 0000000..b17341c --- /dev/null +++ b/02-array-seq/lispy/original/lispy.py @@ -0,0 +1,316 @@ +################ Scheme Interpreter in Python + +## (c) Peter Norvig, 2010; See http://norvig.com/lispy2.html + +################ Symbol, Procedure, classes + +import re, sys, io + +class Symbol(str): pass + +def Sym(s, symbol_table={}): + "Find or create unique Symbol entry for str s in symbol table." + if s not in symbol_table: symbol_table[s] = Symbol(s) + return symbol_table[s] + +_quote, _if, _set, _define, _lambda, _begin, _definemacro, = map(Sym, +"quote if set! define lambda begin define-macro".split()) + +_quasiquote, _unquote, _unquotesplicing = map(Sym, +"quasiquote unquote unquote-splicing".split()) + +class Procedure: + "A user-defined Scheme procedure." + def __init__(self, parms, exp, env): + self.parms, self.exp, self.env = parms, exp, env + def __call__(self, *args): + return eval(self.exp, Env(self.parms, args, self.env)) + +################ parse, read, and user interaction + +def parse(inport): + "Parse a program: read and expand/error-check it." + # Backwards compatibility: given a str, convert it to an InPort + if isinstance(inport, str): inport = InPort(io.StringIO(inport)) + return expand(read(inport), toplevel=True) + +eof_object = Symbol('#') # Note: uninterned; can't be read + +class InPort: + "An input port. Retains a line of chars." + tokenizer = r"""\s*(,@|[('`,)]|"(?:[\\].|[^\\"])*"|;.*|[^\s('"`,;)]*)(.*)""" + def __init__(self, file): + self.file = file; self.line = '' + def next_token(self): + "Return the next token, reading new text into line buffer if needed." + while True: + if self.line == '': self.line = self.file.readline() + if self.line == '': return eof_object + token, self.line = re.match(InPort.tokenizer, self.line).groups() + if token != '' and not token.startswith(';'): + return token + +def readchar(inport): + "Read the next character from an input port." + if inport.line != '': + ch, inport.line = inport.line[0], inport.line[1:] + return ch + else: + return inport.file.read(1) or eof_object + +def read(inport): + "Read a Scheme expression from an input port." + def read_ahead(token): + if '(' == token: + L = [] + while True: + token = inport.next_token() + if token == ')': return L + else: L.append(read_ahead(token)) + elif ')' == token: raise SyntaxError('unexpected )') + elif token in quotes: return [quotes[token], read(inport)] + elif token is eof_object: raise SyntaxError('unexpected EOF in list') + else: return atom(token) + # body of read: + token1 = inport.next_token() + return eof_object if token1 is eof_object else read_ahead(token1) + +quotes = {"'":_quote, "`":_quasiquote, ",":_unquote, ",@":_unquotesplicing} + +def atom(token): + 'Numbers become numbers; #t and #f are booleans; "..." string; otherwise Symbol.' + if token == '#t': return True + elif token == '#f': return False + elif token[0] == '"': return token[1:-1] + try: return int(token) + except ValueError: + try: return float(token) + except ValueError: + try: return complex(token.replace('i', 'j', 1)) + except ValueError: + return Sym(token) + +def to_string(x): + "Convert a Python object back into a Lisp-readable string." + if x is True: return "#t" + elif x is False: return "#f" + elif isa(x, Symbol): return x + elif isa(x, str): return repr(x) + elif isa(x, list): return '('+' '.join(map(to_string, x))+')' + elif isa(x, complex): return str(x).replace('j', 'i') + else: return str(x) + +def load(filename): + "Eval every expression from a file." + repl(None, InPort(open(filename)), None) + +def repl(prompt='lispy> ', inport=InPort(sys.stdin), out=sys.stdout): + "A prompt-read-eval-print loop." + sys.stderr.write("Lispy version 2.0\n") + while True: + try: + if prompt: sys.stderr.write(prompt) + x = parse(inport) + if x is eof_object: return + val = eval(x) + if val is not None and out: print(to_string(val), file=out) + except Exception as e: + print('%s: %s' % (type(e).__name__, e)) + +################ Environment class + +class Env(dict): + "An environment: a dict of {'var':val} pairs, with an outer Env." + def __init__(self, parms=(), args=(), outer=None): + # Bind parm list to corresponding args, or single parm to list of args + self.outer = outer + if isa(parms, Symbol): + self.update({parms:list(args)}) + else: + if len(args) != len(parms): + raise TypeError('expected %s, given %s, ' + % (to_string(parms), to_string(args))) + self.update(zip(parms,args)) + def find(self, var): + "Find the innermost Env where var appears." + if var in self: return self + elif self.outer is None: raise LookupError(var) + else: return self.outer.find(var) + +def is_pair(x): return x != [] and isa(x, list) +def cons(x, y): return [x]+y + +def callcc(proc): + "Call proc with current continuation; escape only" + ball = RuntimeWarning("Sorry, can't continue this continuation any longer.") + def throw(retval): ball.retval = retval; raise ball + try: + return proc(throw) + except RuntimeWarning as w: + if w is ball: return ball.retval + else: raise w + +def add_globals(self): + "Add some Scheme standard procedures." + import math, cmath, operator as op + self.update(vars(math)) + self.update(vars(cmath)) + self.update({ + '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, 'not':op.not_, + '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, + 'equal?':op.eq, 'eq?':op.is_, 'length':len, 'cons':cons, + 'car':lambda x:x[0], 'cdr':lambda x:x[1:], 'append':op.add, + 'list':lambda *x:list(x), 'list?': lambda x:isa(x,list), + 'null?':lambda x:x==[], 'symbol?':lambda x: isa(x, Symbol), + 'boolean?':lambda x: isa(x, bool), 'pair?':is_pair, + 'port?': lambda x:isa(x,file), 'apply':lambda proc,l: proc(*l), + 'eval':lambda x: eval(expand(x)), 'load':lambda fn: load(fn), 'call/cc':callcc, + 'open-input-file':open,'close-input-port':lambda p: p.file.close(), + 'open-output-file':lambda f:open(f,'w'), 'close-output-port':lambda p: p.close(), + 'eof-object?':lambda x:x is eof_object, 'read-char':readchar, + 'read':read, 'write':lambda x,port=sys.stdout:port.write(to_string(x)), + 'display':lambda x,port=sys.stdout:port.write(x if isa(x,str) else to_string(x))}) + return self + +isa = isinstance + +global_env = add_globals(Env()) + +################ eval (tail recursive) + +def eval(x, env=global_env): + "Evaluate an expression in an environment." + while True: + if isa(x, Symbol): # variable reference + return env.find(x)[x] + elif not isa(x, list): # constant literal + return x + elif x[0] is _quote: # (quote exp) + (_, exp) = x + return exp + elif x[0] is _if: # (if test conseq alt) + (_, test, conseq, alt) = x + x = (conseq if eval(test, env) else alt) + elif x[0] is _set: # (set! var exp) + (_, var, exp) = x + env.find(var)[var] = eval(exp, env) + return None + elif x[0] is _define: # (define var exp) + (_, var, exp) = x + env[var] = eval(exp, env) + return None + elif x[0] is _lambda: # (lambda (var*) exp) + (_, vars, exp) = x + return Procedure(vars, exp, env) + elif x[0] is _begin: # (begin exp+) + for exp in x[1:-1]: + eval(exp, env) + x = x[-1] + else: # (proc exp*) + exps = [eval(exp, env) for exp in x] + proc = exps.pop(0) + if isa(proc, Procedure): + x = proc.exp + env = Env(proc.parms, exps, proc.env) + else: + return proc(*exps) + +################ expand + +def expand(x, toplevel=False): + "Walk tree of x, making optimizations/fixes, and signaling SyntaxError." + require(x, x!=[]) # () => Error + if not isa(x, list): # constant => unchanged + return x + elif x[0] is _quote: # (quote exp) + require(x, len(x)==2) + return x + elif x[0] is _if: + if len(x)==3: x = x + [None] # (if t c) => (if t c None) + require(x, len(x)==4) + return list(map(expand, x)) + elif x[0] is _set: + require(x, len(x)==3); + var = x[1] # (set! non-var exp) => Error + require(x, isa(var, Symbol), "can set! only a symbol") + return [_set, var, expand(x[2])] + elif x[0] is _define or x[0] is _definemacro: + require(x, len(x)>=3) + _def, v, body = x[0], x[1], x[2:] + if isa(v, list) and v: # (define (f args) body) + f, args = v[0], v[1:] # => (define f (lambda (args) body)) + return expand([_def, f, [_lambda, args]+body]) + else: + require(x, len(x)==3) # (define non-var/list exp) => Error + require(x, isa(v, Symbol), "can define only a symbol") + exp = expand(x[2]) + if _def is _definemacro: + require(x, toplevel, "define-macro only allowed at top level") + proc = eval(exp) + require(x, callable(proc), "macro must be a procedure") + macro_table[v] = proc # (define-macro v proc) + return None # => None; add v:proc to macro_table + return [_define, v, exp] + elif x[0] is _begin: + if len(x)==1: return None # (begin) => None + else: return [expand(xi, toplevel) for xi in x] + elif x[0] is _lambda: # (lambda (x) e1 e2) + require(x, len(x)>=3) # => (lambda (x) (begin e1 e2)) + vars, body = x[1], x[2:] + require(x, (isa(vars, list) and all(isa(v, Symbol) for v in vars)) + or isa(vars, Symbol), "illegal lambda argument list") + exp = body[0] if len(body) == 1 else [_begin] + body + return [_lambda, vars, expand(exp)] + elif x[0] is _quasiquote: # `x => expand_quasiquote(x) + require(x, len(x)==2) + return expand_quasiquote(x[1]) + elif isa(x[0], Symbol) and x[0] in macro_table: + return expand(macro_table[x[0]](*x[1:]), toplevel) # (m arg...) + else: # => macroexpand if m isa macro + return list(map(expand, x)) # (f arg...) => expand each + +def require(x, predicate, msg="wrong length"): + "Signal a syntax error if predicate is false." + if not predicate: raise SyntaxError(to_string(x)+': '+msg) + +_append, _cons, _let = map(Sym, "append cons let".split()) + +def expand_quasiquote(x): + """Expand `x => 'x; `,x => x; `(,@x y) => (append x y) """ + if not is_pair(x): + return [_quote, x] + require(x, x[0] is not _unquotesplicing, "can't splice here") + if x[0] is _unquote: + require(x, len(x)==2) + return x[1] + elif is_pair(x[0]) and x[0][0] is _unquotesplicing: + require(x[0], len(x[0])==2) + return [_append, x[0][1], expand_quasiquote(x[1:])] + else: + return [_cons, expand_quasiquote(x[0]), expand_quasiquote(x[1:])] + +def let(*args): + args = list(args) + x = cons(_let, args) + require(x, len(args)>1) + bindings, body = args[0], args[1:] + require(x, all(isa(b, list) and len(b)==2 and isa(b[0], Symbol) + for b in bindings), "illegal binding list") + vars, vals = zip(*bindings) + return [[_lambda, list(vars)]+list(map(expand, body))] + list(map(expand, vals)) + +macro_table = {_let:let} ## More macros can go here + +eval(parse("""(begin + +(define-macro and (lambda args + (if (null? args) #t + (if (= (length args) 1) (car args) + `(if ,(car args) (and ,@(cdr args)) #f))))) + +;; More macros can also go here + +)""")) + +if __name__ == '__main__': + repl() diff --git a/02-array-seq/lispy/original/lispytest.py b/02-array-seq/lispy/original/lispytest.py new file mode 100644 index 0000000..b324ff3 --- /dev/null +++ b/02-array-seq/lispy/original/lispytest.py @@ -0,0 +1,122 @@ +from __future__ import print_function + +################ Tests for lis.py and lispy.py + +lis_tests = [ + ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), + ("(+ 2 2)", 4), + ("(+ (* 2 100) (* 1 10))", 210), + ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), + ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), + ("(define x 3)", None), ("x", 3), ("(+ x x)", 6), + ("((lambda (x) (+ x x)) 5)", 10), + ("(define twice (lambda (x) (* 2 x)))", None), ("(twice 5)", 10), + ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), + ("((compose list twice) 5)", [10]), + ("(define repeat (lambda (f) (compose f f)))", None), + ("((repeat twice) 5)", 20), ("((repeat (repeat twice)) 5)", 80), + ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), + ("(fact 3)", 6), + ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), + ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), + ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), + ("""(define combine (lambda (f) + (lambda (x y) + (if (null? x) (quote ()) + (f (list (car x) (car y)) + ((combine f) (cdr x) (cdr y)))))))""", None), + ("(define zip (combine cons))", None), + ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), + ("""(define riff-shuffle (lambda (deck) (begin + (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) + (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) + (define mid (lambda (seq) (/ (length seq) 2))) + ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), + ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), + ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), + ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), + ] + +lispy_tests = [ + ("()", SyntaxError), ("(set! x)", SyntaxError), + ("(define 3 4)", SyntaxError), + ("(quote 1 2)", SyntaxError), ("(if 1 2 3 4)", SyntaxError), + ("(lambda 3 3)", SyntaxError), ("(lambda (x))", SyntaxError), + ("""(if (= 1 2) (define-macro a 'a) + (define-macro a 'b))""", SyntaxError), + ("(define (twice x) (* 2 x))", None), ("(twice 2)", 4), + ("(twice 2 2)", TypeError), + ("(define lyst (lambda items items))", None), + ("(lyst 1 2 3 (+ 2 2))", [1,2,3,4]), + ("(if 1 2)", 2), + ("(if (= 3 4) 2)", None), + ("(begin (define x 1) (set! x (+ x 1)) (+ x 1))", 3), + ("(define ((account bal) amt) (set! bal (+ bal amt)) bal)", None), + ("(define a1 (account 100))", None), + ("(a1 0)", 100), ("(a1 10)", 110), ("(a1 10)", 120), + ("""(define (newton guess function derivative epsilon) + (define guess2 (- guess (/ (function guess) (derivative guess)))) + (if (< (abs (- guess guess2)) epsilon) guess2 + (newton guess2 function derivative epsilon)))""", None), + ("""(define (square-root a) + (newton 1 (lambda (x) (- (* x x) a)) (lambda (x) (* 2 x)) 1e-8))""", None), + ("(> (square-root 200.) 14.14213)", True), + ("(< (square-root 200.) 14.14215)", True), + ("(= (square-root 200.) (sqrt 200.))", True), + ("""(define (sum-squares-range start end) + (define (sumsq-acc start end acc) + (if (> start end) acc (sumsq-acc (+ start 1) end (+ (* start start) acc)))) + (sumsq-acc start end 0))""", None), + ("(sum-squares-range 1 3000)", 9004500500), ## Tests tail recursion + ("(call/cc (lambda (throw) (+ 5 (* 10 (throw 1))))) ;; throw", 1), + ("(call/cc (lambda (throw) (+ 5 (* 10 1)))) ;; do not throw", 15), + ("""(call/cc (lambda (throw) + (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (escape 3)))))))) ; 1 level""", 35), + ("""(call/cc (lambda (throw) + (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (throw 3)))))))) ; 2 levels""", 3), + ("""(call/cc (lambda (throw) + (+ 5 (* 10 (call/cc (lambda (escape) (* 100 1))))))) ; 0 levels""", 1005), + ("(* 1i 1i)", -1), ("(sqrt -1)", 1j), + ("(let ((a 1) (b 2)) (+ a b))", 3), + ("(let ((a 1) (b 2 3)) (+ a b))", SyntaxError), + ("(and 1 2 3)", 3), ("(and (> 2 1) 2 3)", 3), ("(and)", True), + ("(and (> 2 1) (> 2 3))", False), + ("(define-macro unless (lambda args `(if (not ,(car args)) (begin ,@(cdr args))))) ; test `", None), + ("(unless (= 2 (+ 1 1)) (display 2) 3 4)", None), + (r'(unless (= 4 (+ 1 1)) (display 2) (display "\n") 3 4)', 4), + ("(quote x)", 'x'), + ("(quote (1 2 three))", [1, 2, 'three']), + ("'x", 'x'), + ("'(one 2 3)", ['one', 2, 3]), + ("(define L (list 1 2 3))", None), + ("`(testing ,@L testing)", ['testing',1,2,3,'testing']), + ("`(testing ,L testing)", ['testing',[1,2,3],'testing']), + ("`,@L", SyntaxError), + ("""'(1 ;test comments ' + ;skip this line + 2 ; more ; comments ; ) ) + 3) ; final comment""", [1,2,3]), + ] + +def test(tests, name=''): + "For each (exp, expected) test case, see if eval(parse(exp)) == expected." + fails = 0 + for (x, expected) in tests: + try: + result = eval(parse(x)) + print(x, '=>', lispstr(result)) + ok = (result == expected) + except Exception as e: + print(x, '=raises=>', type(e).__name__, e) + ok = isinstance(expected, type) and issubclass(expected, Exception) and isinstance(e, expected) + if not ok: + fails += 1 + print('FAIL!!! Expected', expected) + print('%s %s: %d out of %d tests fail.' % ('*'*45, name, fails, len(tests))) + +if __name__ == '__main__': + from lis import * + test(lis_tests, 'lis.py') + from lispy import * + test(lis_tests+lispy_tests, 'lispy.py') + diff --git a/02-array-seq/lispy/examples_test.py b/02-array-seq/lispy/py3.10/examples_test.py similarity index 100% rename from 02-array-seq/lispy/examples_test.py rename to 02-array-seq/lispy/py3.10/examples_test.py diff --git a/02-array-seq/lispy/lis.py b/02-array-seq/lispy/py3.10/lis.py similarity index 95% rename from 02-array-seq/lispy/lis.py rename to 02-array-seq/lispy/py3.10/lis.py index e37ba23..7ae0a9d 100644 --- a/02-array-seq/lispy/lis.py +++ b/02-array-seq/lispy/py3.10/lis.py @@ -23,7 +23,7 @@ class Procedure: "A user-defined Scheme procedure." - def __init__(self, parms: list[Symbol], body: Expression, env: Environment): + def __init__(self, parms: list[Symbol], body: list[Expression], env: Environment): self.parms = parms self.body = body self.env = env @@ -31,7 +31,9 @@ def __init__(self, parms: list[Symbol], body: Expression, env: Environment): def __call__(self, *args: Expression) -> Any: local_env = dict(zip(self.parms, args)) env: Environment = ChainMap(local_env, self.env) - return evaluate(self.body, env) + for exp in self.body: + result = evaluate(exp, env) + return result ################ global environment @@ -160,9 +162,9 @@ def evaluate(exp: Expression, env: Environment) -> Any: return evaluate(alternative, env) case ['define', Symbol(var), value_exp]: env[var] = evaluate(value_exp, env) - case ['define', [Symbol(name), *parms], body]: + case ['define', [Symbol(name), *parms], *body]: env[name] = Procedure(parms, body, env) - case ['lambda', [*parms], body]: + case ['lambda', [*parms], *body]: return Procedure(parms, body, env) case [op, *args]: proc = evaluate(op, env) diff --git a/02-array-seq/lispy/lis_test.py b/02-array-seq/lispy/py3.10/lis_test.py similarity index 97% rename from 02-array-seq/lispy/lis_test.py rename to 02-array-seq/lispy/py3.10/lis_test.py index e220e74..3688888 100644 --- a/02-array-seq/lispy/lis_test.py +++ b/02-array-seq/lispy/py3.10/lis_test.py @@ -12,7 +12,7 @@ ('(sum 1 2 3)', ['sum', 1, 2, 3]), ('(+ (* 2 100) (* 1 10))', ['+', ['*', 2, 100], ['*', 1, 10]]), ('99 100', 99), # parse stops at the first complete expression - ('(a)(b)', ['a']), + ('(a)(b)', ['a']), ]) def test_parse(source: str, expected: Expression) -> None: got = parse(source) @@ -122,7 +122,7 @@ def test_lambda(std_env: Environment) -> None: source = '(lambda (a b) (if (>= a b) a b))' func = evaluate(parse(source), std_env) assert func.parms == ['a', 'b'] - assert func.body == ['if', ['>=', 'a', 'b'], 'a', 'b'] + assert func.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] assert func.env is std_env assert func(1, 2) == 2 assert func(3, 2) == 3 @@ -176,7 +176,7 @@ def test_define_function(std_env: Environment) -> None: assert got is None max_fn = std_env['max'] assert max_fn.parms == ['a', 'b'] - assert max_fn.body == ['if', ['>=', 'a', 'b'], 'a', 'b'] + assert max_fn.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] assert max_fn.env is std_env assert max_fn(1, 2) == 2 assert max_fn(3, 2) == 3 diff --git a/02-array-seq/lispy/meta_test.py b/02-array-seq/lispy/py3.10/meta_test.py similarity index 100% rename from 02-array-seq/lispy/meta_test.py rename to 02-array-seq/lispy/py3.10/meta_test.py From de1fbfbcee6238f4c33cf9d3ea2e5afe2c6313d4 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Tue, 29 Jun 2021 12:24:32 -0300 Subject: [PATCH 051/127] Update README.md --- 02-array-seq/lispy/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/02-array-seq/lispy/README.md b/02-array-seq/lispy/README.md index 0c11fd8..b8f769a 100644 --- a/02-array-seq/lispy/README.md +++ b/02-array-seq/lispy/README.md @@ -1,4 +1,4 @@ -# Norvig's originals and updates +# Norvig's `lis.py` and updates This directory contains: @@ -33,4 +33,4 @@ I made small changes to the programs in `original/`: * In `lispy.py` I made [changes and a pull request](https://github.com/norvig/pytudes/pull/106) to make it run on Python 3. -_Luciano Ramalho
June 29, 2021_ \ No newline at end of file +_Luciano Ramalho
June 29, 2021_ From 88a92385528b67c3c9e90c7ef2466aef18850791 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Tue, 29 Jun 2021 12:26:50 -0300 Subject: [PATCH 052/127] Update README.md --- 02-array-seq/lispy/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/02-array-seq/lispy/README.md b/02-array-seq/lispy/README.md index b8f769a..be251f2 100644 --- a/02-array-seq/lispy/README.md +++ b/02-array-seq/lispy/README.md @@ -8,7 +8,7 @@ Norvig's [`lis.py`](https://github.com/norvig/pytudes/blob/c33cd6835a506a57d9fe7 * `py3.10/`: `lis.py` with type hints, pattern matching, and minor edits—requires Python 3.10. The `py3.10/` directory also has `lis_test.py` to run with -[pytest](https://docs.pytest.org), including all the +[pytest](https://docs.pytest.org), including the [`lis_tests` suite](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/py/lispytest.py#L5) from `original/lispytest.py`, and additional separate tests for each expression and special form handled by `evaluate`. @@ -28,7 +28,7 @@ The copyright holder is Peter Norvig and the code is licensed under the I made small changes to the programs in `original/`: * In `lis.py`: - * The `Procedure` class accepts a list of expressions as the `body`, and `__call__` evaluates all those expressions in order, returning the value of the last. This is consistent with Scheme's `lambda` syntax and provided a useful example for pattern matching. + * The `Procedure` class accepts a list of expressions as the `body`, and `__call__` evaluates those expressions in order, and returns the value of the last. This is consistent with Scheme's `lambda` syntax and provided a useful example for pattern matching. * In the `elif` block for `'lambda'`, I added the `*` in front of the `*body` variable in the tuple unpacking to capture the expressions as a list, before calling the `Procedure` constructor. * In `lispy.py` I made [changes and a pull request](https://github.com/norvig/pytudes/pull/106) to make it run on Python 3. From 23e78eeb82e04731fbb6063d1075d2f6400cc26c Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 7 Jul 2021 23:45:54 -0300 Subject: [PATCH 053/127] sync with O'Reilly Atlas --- 02-array-seq/lispy/LICENSE | 21 ++ 02-array-seq/lispy/README.md | 36 ++ 02-array-seq/lispy/original/LICENSE | 21 ++ 02-array-seq/lispy/original/README.md | 8 + 02-array-seq/lispy/original/lis.py | 132 ++++++++ 02-array-seq/lispy/original/lispy.py | 316 ++++++++++++++++++ 02-array-seq/lispy/original/lispytest.py | 122 +++++++ .../lispy/{ => py3.10}/examples_test.py | 0 02-array-seq/lispy/py3.10/lis.py | 188 +++++++++++ 02-array-seq/lispy/{ => py3.10}/lis_test.py | 6 +- 02-array-seq/lispy/py3.10/meta_test.py | 69 ++++ 02-array-seq/lispy/py3.9-no-hints/README.md | 33 ++ 02-array-seq/lispy/py3.9-no-hints/lis.py | 142 ++++++++ 02-array-seq/lispy/py3.9-no-hints/lis_test.py | 166 +++++++++ 02-array-seq/lispy/py3.9/README.md | 33 ++ 02-array-seq/lispy/py3.9/lis.py | 155 +++++++++ 02-array-seq/lispy/py3.9/lis_test.py | 168 ++++++++++ 02-array-seq/match_lat_lon.py | 4 +- 02-array-seq/metro_lat_lon.py | 8 +- 03-dict-set/py3.10/creator.py | 40 +++ 04-text-byte/categories.py | 4 +- 04-text-byte/stdout_check.py | 6 +- .../README.asciidoc | 0 .../cards.doctest | 0 {05-record-like => 05-data-classes}/cards.py | 0 .../cards_enum.py | 0 .../class/coordinates.py | 2 +- .../dataclass/club.py | 0 .../dataclass/club_generic.py | 0 .../dataclass/club_wrong.py | 0 .../dataclass/coordinates.py | 0 .../dataclass/hackerclub.py | 0 .../dataclass/hackerclub_annotated.py | 0 .../dataclass/resource.py | 0 .../dataclass/resource_repr.py | 0 .../frenchdeck.doctest | 0 .../frenchdeck.py | 0 05-data-classes/match_cities.py | 92 +++++ .../meaning/demo_dc.py | 0 .../meaning/demo_nt.py | 0 .../meaning/demo_plain.py | 0 .../typing_namedtuple/coordinates.py | 0 .../typing_namedtuple/coordinates2.py | 2 +- .../typing_namedtuple/nocheck_demo.py | 2 +- 05-record-like/struct/README | 17 - 05-record-like/struct/metro | Bin 8764 -> 0 bytes 05-record-like/struct/metro_areas.bin | Bin 72 -> 0 bytes 05-record-like/struct/metro_read.py | 15 - 05-record-like/struct/metro_write.c | 46 --- 07-1class-func/clip.py | 30 +- 07-1class-func/clip_signature.rst | 4 +- 07-1class-func/tagger.py | 9 +- {18-context-mngr => 18-with-match}/README.rst | 0 18-with-match/lisplus/examples_test.py | 109 ++++++ .../lispy => 18-with-match/lisplus}/lis.py | 10 +- 18-with-match/lisplus/lis_test.py | 182 ++++++++++ .../lisplus}/meta_test.py | 0 {18-context-mngr => 18-with-match}/mirror.py | 0 .../mirror_gen.py | 0 .../mirror_gen_exc.py | 0 .../checked/decorator/checkeddeco.py | 3 +- .../checked/initsub/checkedlib.py | 3 +- .../checked/metaclass/checkedlib.py | 3 +- README.md | 4 +- 64 files changed, 2087 insertions(+), 124 deletions(-) create mode 100644 02-array-seq/lispy/LICENSE create mode 100644 02-array-seq/lispy/README.md create mode 100644 02-array-seq/lispy/original/LICENSE create mode 100644 02-array-seq/lispy/original/README.md create mode 100644 02-array-seq/lispy/original/lis.py create mode 100644 02-array-seq/lispy/original/lispy.py create mode 100644 02-array-seq/lispy/original/lispytest.py rename 02-array-seq/lispy/{ => py3.10}/examples_test.py (100%) create mode 100644 02-array-seq/lispy/py3.10/lis.py rename 02-array-seq/lispy/{ => py3.10}/lis_test.py (97%) create mode 100644 02-array-seq/lispy/py3.10/meta_test.py create mode 100644 02-array-seq/lispy/py3.9-no-hints/README.md create mode 100644 02-array-seq/lispy/py3.9-no-hints/lis.py create mode 100644 02-array-seq/lispy/py3.9-no-hints/lis_test.py create mode 100644 02-array-seq/lispy/py3.9/README.md create mode 100644 02-array-seq/lispy/py3.9/lis.py create mode 100644 02-array-seq/lispy/py3.9/lis_test.py create mode 100644 03-dict-set/py3.10/creator.py rename {05-record-like => 05-data-classes}/README.asciidoc (100%) rename {05-record-like => 05-data-classes}/cards.doctest (100%) rename {05-record-like => 05-data-classes}/cards.py (100%) rename {05-record-like => 05-data-classes}/cards_enum.py (100%) rename {05-record-like => 05-data-classes}/class/coordinates.py (94%) rename {05-record-like => 05-data-classes}/dataclass/club.py (100%) rename {05-record-like => 05-data-classes}/dataclass/club_generic.py (100%) rename {05-record-like => 05-data-classes}/dataclass/club_wrong.py (100%) rename {05-record-like => 05-data-classes}/dataclass/coordinates.py (100%) rename {05-record-like => 05-data-classes}/dataclass/hackerclub.py (100%) rename {05-record-like => 05-data-classes}/dataclass/hackerclub_annotated.py (100%) rename {05-record-like => 05-data-classes}/dataclass/resource.py (100%) rename {05-record-like => 05-data-classes}/dataclass/resource_repr.py (100%) rename {05-record-like => 05-data-classes}/frenchdeck.doctest (100%) rename {05-record-like => 05-data-classes}/frenchdeck.py (100%) create mode 100644 05-data-classes/match_cities.py rename {05-record-like => 05-data-classes}/meaning/demo_dc.py (100%) rename {05-record-like => 05-data-classes}/meaning/demo_nt.py (100%) rename {05-record-like => 05-data-classes}/meaning/demo_plain.py (100%) rename {05-record-like => 05-data-classes}/typing_namedtuple/coordinates.py (100%) rename {05-record-like => 05-data-classes}/typing_namedtuple/coordinates2.py (95%) rename {05-record-like => 05-data-classes}/typing_namedtuple/nocheck_demo.py (71%) delete mode 100644 05-record-like/struct/README delete mode 100755 05-record-like/struct/metro delete mode 100644 05-record-like/struct/metro_areas.bin delete mode 100644 05-record-like/struct/metro_read.py delete mode 100644 05-record-like/struct/metro_write.c rename {18-context-mngr => 18-with-match}/README.rst (100%) create mode 100644 18-with-match/lisplus/examples_test.py rename {02-array-seq/lispy => 18-with-match/lisplus}/lis.py (95%) create mode 100644 18-with-match/lisplus/lis_test.py rename {02-array-seq/lispy => 18-with-match/lisplus}/meta_test.py (100%) rename {18-context-mngr => 18-with-match}/mirror.py (100%) rename {18-context-mngr => 18-with-match}/mirror_gen.py (100%) rename {18-context-mngr => 18-with-match}/mirror_gen_exc.py (100%) diff --git a/02-array-seq/lispy/LICENSE b/02-array-seq/lispy/LICENSE new file mode 100644 index 0000000..ca550a2 --- /dev/null +++ b/02-array-seq/lispy/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2010-2017 Peter Norvig + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/02-array-seq/lispy/README.md b/02-array-seq/lispy/README.md new file mode 100644 index 0000000..0c11fd8 --- /dev/null +++ b/02-array-seq/lispy/README.md @@ -0,0 +1,36 @@ +# Norvig's originals and updates + +This directory contains: + +* `original/`: +Norvig's [`lis.py`](https://github.com/norvig/pytudes/blob/c33cd6835a506a57d9fe73e3a8317d49babb13e8/py/lis.py), +[`lispy.py`](https://github.com/norvig/pytudes/blob/c33cd6835a506a57d9fe73e3a8317d49babb13e8/py/lispy.py), and the `lispytest.py` custom test script for testing both; +* `py3.10/`: `lis.py` with type hints, pattern matching, and minor edits—requires Python 3.10. + +The `py3.10/` directory also has `lis_test.py` to run with +[pytest](https://docs.pytest.org), including all the +[`lis_tests` suite](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/py/lispytest.py#L5) +from `original/lispytest.py`, +and additional separate tests for each expression and special form handled by `evaluate`. + + +## Provenance, Copyright and License + +`lis.py` is +[published](https://github.com/norvig/pytudes/blob/c33cd6835a506a57d9fe73e3a8317d49babb13e8/py/lis.py) +in the [norvig/pytudes](https://github.com/norvig/pytudes) repository on Github. +The copyright holder is Peter Norvig and the code is licensed under the +[MIT license](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/LICENSE). + + +## Changes to Norvig's code + +I made small changes to the programs in `original/`: + +* In `lis.py`: + * The `Procedure` class accepts a list of expressions as the `body`, and `__call__` evaluates all those expressions in order, returning the value of the last. This is consistent with Scheme's `lambda` syntax and provided a useful example for pattern matching. + * In the `elif` block for `'lambda'`, I added the `*` in front of the `*body` variable in the tuple unpacking to capture the expressions as a list, before calling the `Procedure` constructor. + +* In `lispy.py` I made [changes and a pull request](https://github.com/norvig/pytudes/pull/106) to make it run on Python 3. + +_Luciano Ramalho
June 29, 2021_ \ No newline at end of file diff --git a/02-array-seq/lispy/original/LICENSE b/02-array-seq/lispy/original/LICENSE new file mode 100644 index 0000000..ca550a2 --- /dev/null +++ b/02-array-seq/lispy/original/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2010-2017 Peter Norvig + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/02-array-seq/lispy/original/README.md b/02-array-seq/lispy/original/README.md new file mode 100644 index 0000000..bb4d4e0 --- /dev/null +++ b/02-array-seq/lispy/original/README.md @@ -0,0 +1,8 @@ + +# Source of the originals + +* [lis.py](https://raw.githubusercontent.com/norvig/pytudes/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lis.py) + +* [lispy.py](https://raw.githubusercontent.com/norvig/pytudes/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispy.py) + +* [lispytest.py](https://raw.githubusercontent.com/norvig/pytudes/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispytest.py) diff --git a/02-array-seq/lispy/original/lis.py b/02-array-seq/lispy/original/lis.py new file mode 100644 index 0000000..f81376a --- /dev/null +++ b/02-array-seq/lispy/original/lis.py @@ -0,0 +1,132 @@ +################ Lispy: Scheme Interpreter in Python 3.3+ + +## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html + +################ Imports and Types + +import math +import operator as op +from collections import ChainMap as Environment + +Symbol = str # A Lisp Symbol is implemented as a Python str +List = list # A Lisp List is implemented as a Python list +Number = (int, float) # A Lisp Number is implemented as a Python int or float + +class Procedure(object): + "A user-defined Scheme procedure." + def __init__(self, parms, body, env): + self.parms, self.body, self.env = parms, body, env + def __call__(self, *args): + env = Environment(dict(zip(self.parms, args)), self.env) + return eval(self.body, env) + +################ Global Environment + +def standard_env(): + "An environment with some Scheme standard procedures." + env = {} + env.update(vars(math)) # sin, cos, sqrt, pi, ... + env.update({ + '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, + '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, + 'abs': abs, + 'append': op.add, + 'apply': lambda proc, args: proc(*args), + 'begin': lambda *x: x[-1], + 'car': lambda x: x[0], + 'cdr': lambda x: x[1:], + 'cons': lambda x,y: [x] + y, + 'eq?': op.is_, + 'equal?': op.eq, + 'length': len, + 'list': lambda *x: list(x), + 'list?': lambda x: isinstance(x,list), + 'map': lambda *args: list(map(*args)), + 'max': max, + 'min': min, + 'not': op.not_, + 'null?': lambda x: x == [], + 'number?': lambda x: isinstance(x, Number), + 'procedure?': callable, + 'round': round, + 'symbol?': lambda x: isinstance(x, Symbol), + }) + return env + +global_env = standard_env() + +################ Parsing: parse, tokenize, and read_from_tokens + +def parse(program): + "Read a Scheme expression from a string." + return read_from_tokens(tokenize(program)) + +def tokenize(s): + "Convert a string into a list of tokens." + return s.replace('(',' ( ').replace(')',' ) ').split() + +def read_from_tokens(tokens): + "Read an expression from a sequence of tokens." + if len(tokens) == 0: + raise SyntaxError('unexpected EOF while reading') + token = tokens.pop(0) + if '(' == token: + L = [] + while tokens[0] != ')': + L.append(read_from_tokens(tokens)) + tokens.pop(0) # pop off ')' + return L + elif ')' == token: + raise SyntaxError('unexpected )') + else: + return atom(token) + +def atom(token): + "Numbers become numbers; every other token is a symbol." + try: return int(token) + except ValueError: + try: return float(token) + except ValueError: + return Symbol(token) + +################ Interaction: A REPL + +def repl(prompt='lis.py> '): + "A prompt-read-eval-print loop." + while True: + val = eval(parse(input(prompt))) + if val is not None: + print(lispstr(val)) + +def lispstr(exp): + "Convert a Python object back into a Lisp-readable string." + if isinstance(exp, List): + return '(' + ' '.join(map(lispstr, exp)) + ')' + else: + return str(exp) + +################ eval + +def eval(x, env=global_env): + "Evaluate an expression in an environment." + if isinstance(x, Symbol): # variable reference + return env[x] + elif not isinstance(x, List): # constant literal + return x + elif x[0] == 'quote': # (quote exp) + (_, exp) = x + return exp + elif x[0] == 'if': # (if test conseq alt) + (_, test, conseq, alt) = x + exp = (conseq if eval(test, env) else alt) + return eval(exp, env) + elif x[0] == 'define': # (define var exp) + (_, var, exp) = x + env[var] = eval(exp, env) + elif x[0] == 'lambda': # (lambda (var...) body) + (_, parms, body) = x + return Procedure(parms, body, env) + else: # (proc arg...) + proc = eval(x[0], env) + args = [eval(exp, env) for exp in x[1:]] + return proc(*args) diff --git a/02-array-seq/lispy/original/lispy.py b/02-array-seq/lispy/original/lispy.py new file mode 100644 index 0000000..b17341c --- /dev/null +++ b/02-array-seq/lispy/original/lispy.py @@ -0,0 +1,316 @@ +################ Scheme Interpreter in Python + +## (c) Peter Norvig, 2010; See http://norvig.com/lispy2.html + +################ Symbol, Procedure, classes + +import re, sys, io + +class Symbol(str): pass + +def Sym(s, symbol_table={}): + "Find or create unique Symbol entry for str s in symbol table." + if s not in symbol_table: symbol_table[s] = Symbol(s) + return symbol_table[s] + +_quote, _if, _set, _define, _lambda, _begin, _definemacro, = map(Sym, +"quote if set! define lambda begin define-macro".split()) + +_quasiquote, _unquote, _unquotesplicing = map(Sym, +"quasiquote unquote unquote-splicing".split()) + +class Procedure: + "A user-defined Scheme procedure." + def __init__(self, parms, exp, env): + self.parms, self.exp, self.env = parms, exp, env + def __call__(self, *args): + return eval(self.exp, Env(self.parms, args, self.env)) + +################ parse, read, and user interaction + +def parse(inport): + "Parse a program: read and expand/error-check it." + # Backwards compatibility: given a str, convert it to an InPort + if isinstance(inport, str): inport = InPort(io.StringIO(inport)) + return expand(read(inport), toplevel=True) + +eof_object = Symbol('#') # Note: uninterned; can't be read + +class InPort: + "An input port. Retains a line of chars." + tokenizer = r"""\s*(,@|[('`,)]|"(?:[\\].|[^\\"])*"|;.*|[^\s('"`,;)]*)(.*)""" + def __init__(self, file): + self.file = file; self.line = '' + def next_token(self): + "Return the next token, reading new text into line buffer if needed." + while True: + if self.line == '': self.line = self.file.readline() + if self.line == '': return eof_object + token, self.line = re.match(InPort.tokenizer, self.line).groups() + if token != '' and not token.startswith(';'): + return token + +def readchar(inport): + "Read the next character from an input port." + if inport.line != '': + ch, inport.line = inport.line[0], inport.line[1:] + return ch + else: + return inport.file.read(1) or eof_object + +def read(inport): + "Read a Scheme expression from an input port." + def read_ahead(token): + if '(' == token: + L = [] + while True: + token = inport.next_token() + if token == ')': return L + else: L.append(read_ahead(token)) + elif ')' == token: raise SyntaxError('unexpected )') + elif token in quotes: return [quotes[token], read(inport)] + elif token is eof_object: raise SyntaxError('unexpected EOF in list') + else: return atom(token) + # body of read: + token1 = inport.next_token() + return eof_object if token1 is eof_object else read_ahead(token1) + +quotes = {"'":_quote, "`":_quasiquote, ",":_unquote, ",@":_unquotesplicing} + +def atom(token): + 'Numbers become numbers; #t and #f are booleans; "..." string; otherwise Symbol.' + if token == '#t': return True + elif token == '#f': return False + elif token[0] == '"': return token[1:-1] + try: return int(token) + except ValueError: + try: return float(token) + except ValueError: + try: return complex(token.replace('i', 'j', 1)) + except ValueError: + return Sym(token) + +def to_string(x): + "Convert a Python object back into a Lisp-readable string." + if x is True: return "#t" + elif x is False: return "#f" + elif isa(x, Symbol): return x + elif isa(x, str): return repr(x) + elif isa(x, list): return '('+' '.join(map(to_string, x))+')' + elif isa(x, complex): return str(x).replace('j', 'i') + else: return str(x) + +def load(filename): + "Eval every expression from a file." + repl(None, InPort(open(filename)), None) + +def repl(prompt='lispy> ', inport=InPort(sys.stdin), out=sys.stdout): + "A prompt-read-eval-print loop." + sys.stderr.write("Lispy version 2.0\n") + while True: + try: + if prompt: sys.stderr.write(prompt) + x = parse(inport) + if x is eof_object: return + val = eval(x) + if val is not None and out: print(to_string(val), file=out) + except Exception as e: + print('%s: %s' % (type(e).__name__, e)) + +################ Environment class + +class Env(dict): + "An environment: a dict of {'var':val} pairs, with an outer Env." + def __init__(self, parms=(), args=(), outer=None): + # Bind parm list to corresponding args, or single parm to list of args + self.outer = outer + if isa(parms, Symbol): + self.update({parms:list(args)}) + else: + if len(args) != len(parms): + raise TypeError('expected %s, given %s, ' + % (to_string(parms), to_string(args))) + self.update(zip(parms,args)) + def find(self, var): + "Find the innermost Env where var appears." + if var in self: return self + elif self.outer is None: raise LookupError(var) + else: return self.outer.find(var) + +def is_pair(x): return x != [] and isa(x, list) +def cons(x, y): return [x]+y + +def callcc(proc): + "Call proc with current continuation; escape only" + ball = RuntimeWarning("Sorry, can't continue this continuation any longer.") + def throw(retval): ball.retval = retval; raise ball + try: + return proc(throw) + except RuntimeWarning as w: + if w is ball: return ball.retval + else: raise w + +def add_globals(self): + "Add some Scheme standard procedures." + import math, cmath, operator as op + self.update(vars(math)) + self.update(vars(cmath)) + self.update({ + '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, 'not':op.not_, + '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, + 'equal?':op.eq, 'eq?':op.is_, 'length':len, 'cons':cons, + 'car':lambda x:x[0], 'cdr':lambda x:x[1:], 'append':op.add, + 'list':lambda *x:list(x), 'list?': lambda x:isa(x,list), + 'null?':lambda x:x==[], 'symbol?':lambda x: isa(x, Symbol), + 'boolean?':lambda x: isa(x, bool), 'pair?':is_pair, + 'port?': lambda x:isa(x,file), 'apply':lambda proc,l: proc(*l), + 'eval':lambda x: eval(expand(x)), 'load':lambda fn: load(fn), 'call/cc':callcc, + 'open-input-file':open,'close-input-port':lambda p: p.file.close(), + 'open-output-file':lambda f:open(f,'w'), 'close-output-port':lambda p: p.close(), + 'eof-object?':lambda x:x is eof_object, 'read-char':readchar, + 'read':read, 'write':lambda x,port=sys.stdout:port.write(to_string(x)), + 'display':lambda x,port=sys.stdout:port.write(x if isa(x,str) else to_string(x))}) + return self + +isa = isinstance + +global_env = add_globals(Env()) + +################ eval (tail recursive) + +def eval(x, env=global_env): + "Evaluate an expression in an environment." + while True: + if isa(x, Symbol): # variable reference + return env.find(x)[x] + elif not isa(x, list): # constant literal + return x + elif x[0] is _quote: # (quote exp) + (_, exp) = x + return exp + elif x[0] is _if: # (if test conseq alt) + (_, test, conseq, alt) = x + x = (conseq if eval(test, env) else alt) + elif x[0] is _set: # (set! var exp) + (_, var, exp) = x + env.find(var)[var] = eval(exp, env) + return None + elif x[0] is _define: # (define var exp) + (_, var, exp) = x + env[var] = eval(exp, env) + return None + elif x[0] is _lambda: # (lambda (var*) exp) + (_, vars, exp) = x + return Procedure(vars, exp, env) + elif x[0] is _begin: # (begin exp+) + for exp in x[1:-1]: + eval(exp, env) + x = x[-1] + else: # (proc exp*) + exps = [eval(exp, env) for exp in x] + proc = exps.pop(0) + if isa(proc, Procedure): + x = proc.exp + env = Env(proc.parms, exps, proc.env) + else: + return proc(*exps) + +################ expand + +def expand(x, toplevel=False): + "Walk tree of x, making optimizations/fixes, and signaling SyntaxError." + require(x, x!=[]) # () => Error + if not isa(x, list): # constant => unchanged + return x + elif x[0] is _quote: # (quote exp) + require(x, len(x)==2) + return x + elif x[0] is _if: + if len(x)==3: x = x + [None] # (if t c) => (if t c None) + require(x, len(x)==4) + return list(map(expand, x)) + elif x[0] is _set: + require(x, len(x)==3); + var = x[1] # (set! non-var exp) => Error + require(x, isa(var, Symbol), "can set! only a symbol") + return [_set, var, expand(x[2])] + elif x[0] is _define or x[0] is _definemacro: + require(x, len(x)>=3) + _def, v, body = x[0], x[1], x[2:] + if isa(v, list) and v: # (define (f args) body) + f, args = v[0], v[1:] # => (define f (lambda (args) body)) + return expand([_def, f, [_lambda, args]+body]) + else: + require(x, len(x)==3) # (define non-var/list exp) => Error + require(x, isa(v, Symbol), "can define only a symbol") + exp = expand(x[2]) + if _def is _definemacro: + require(x, toplevel, "define-macro only allowed at top level") + proc = eval(exp) + require(x, callable(proc), "macro must be a procedure") + macro_table[v] = proc # (define-macro v proc) + return None # => None; add v:proc to macro_table + return [_define, v, exp] + elif x[0] is _begin: + if len(x)==1: return None # (begin) => None + else: return [expand(xi, toplevel) for xi in x] + elif x[0] is _lambda: # (lambda (x) e1 e2) + require(x, len(x)>=3) # => (lambda (x) (begin e1 e2)) + vars, body = x[1], x[2:] + require(x, (isa(vars, list) and all(isa(v, Symbol) for v in vars)) + or isa(vars, Symbol), "illegal lambda argument list") + exp = body[0] if len(body) == 1 else [_begin] + body + return [_lambda, vars, expand(exp)] + elif x[0] is _quasiquote: # `x => expand_quasiquote(x) + require(x, len(x)==2) + return expand_quasiquote(x[1]) + elif isa(x[0], Symbol) and x[0] in macro_table: + return expand(macro_table[x[0]](*x[1:]), toplevel) # (m arg...) + else: # => macroexpand if m isa macro + return list(map(expand, x)) # (f arg...) => expand each + +def require(x, predicate, msg="wrong length"): + "Signal a syntax error if predicate is false." + if not predicate: raise SyntaxError(to_string(x)+': '+msg) + +_append, _cons, _let = map(Sym, "append cons let".split()) + +def expand_quasiquote(x): + """Expand `x => 'x; `,x => x; `(,@x y) => (append x y) """ + if not is_pair(x): + return [_quote, x] + require(x, x[0] is not _unquotesplicing, "can't splice here") + if x[0] is _unquote: + require(x, len(x)==2) + return x[1] + elif is_pair(x[0]) and x[0][0] is _unquotesplicing: + require(x[0], len(x[0])==2) + return [_append, x[0][1], expand_quasiquote(x[1:])] + else: + return [_cons, expand_quasiquote(x[0]), expand_quasiquote(x[1:])] + +def let(*args): + args = list(args) + x = cons(_let, args) + require(x, len(args)>1) + bindings, body = args[0], args[1:] + require(x, all(isa(b, list) and len(b)==2 and isa(b[0], Symbol) + for b in bindings), "illegal binding list") + vars, vals = zip(*bindings) + return [[_lambda, list(vars)]+list(map(expand, body))] + list(map(expand, vals)) + +macro_table = {_let:let} ## More macros can go here + +eval(parse("""(begin + +(define-macro and (lambda args + (if (null? args) #t + (if (= (length args) 1) (car args) + `(if ,(car args) (and ,@(cdr args)) #f))))) + +;; More macros can also go here + +)""")) + +if __name__ == '__main__': + repl() diff --git a/02-array-seq/lispy/original/lispytest.py b/02-array-seq/lispy/original/lispytest.py new file mode 100644 index 0000000..b324ff3 --- /dev/null +++ b/02-array-seq/lispy/original/lispytest.py @@ -0,0 +1,122 @@ +from __future__ import print_function + +################ Tests for lis.py and lispy.py + +lis_tests = [ + ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), + ("(+ 2 2)", 4), + ("(+ (* 2 100) (* 1 10))", 210), + ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), + ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), + ("(define x 3)", None), ("x", 3), ("(+ x x)", 6), + ("((lambda (x) (+ x x)) 5)", 10), + ("(define twice (lambda (x) (* 2 x)))", None), ("(twice 5)", 10), + ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), + ("((compose list twice) 5)", [10]), + ("(define repeat (lambda (f) (compose f f)))", None), + ("((repeat twice) 5)", 20), ("((repeat (repeat twice)) 5)", 80), + ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), + ("(fact 3)", 6), + ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), + ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), + ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), + ("""(define combine (lambda (f) + (lambda (x y) + (if (null? x) (quote ()) + (f (list (car x) (car y)) + ((combine f) (cdr x) (cdr y)))))))""", None), + ("(define zip (combine cons))", None), + ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), + ("""(define riff-shuffle (lambda (deck) (begin + (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) + (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) + (define mid (lambda (seq) (/ (length seq) 2))) + ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), + ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), + ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), + ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), + ] + +lispy_tests = [ + ("()", SyntaxError), ("(set! x)", SyntaxError), + ("(define 3 4)", SyntaxError), + ("(quote 1 2)", SyntaxError), ("(if 1 2 3 4)", SyntaxError), + ("(lambda 3 3)", SyntaxError), ("(lambda (x))", SyntaxError), + ("""(if (= 1 2) (define-macro a 'a) + (define-macro a 'b))""", SyntaxError), + ("(define (twice x) (* 2 x))", None), ("(twice 2)", 4), + ("(twice 2 2)", TypeError), + ("(define lyst (lambda items items))", None), + ("(lyst 1 2 3 (+ 2 2))", [1,2,3,4]), + ("(if 1 2)", 2), + ("(if (= 3 4) 2)", None), + ("(begin (define x 1) (set! x (+ x 1)) (+ x 1))", 3), + ("(define ((account bal) amt) (set! bal (+ bal amt)) bal)", None), + ("(define a1 (account 100))", None), + ("(a1 0)", 100), ("(a1 10)", 110), ("(a1 10)", 120), + ("""(define (newton guess function derivative epsilon) + (define guess2 (- guess (/ (function guess) (derivative guess)))) + (if (< (abs (- guess guess2)) epsilon) guess2 + (newton guess2 function derivative epsilon)))""", None), + ("""(define (square-root a) + (newton 1 (lambda (x) (- (* x x) a)) (lambda (x) (* 2 x)) 1e-8))""", None), + ("(> (square-root 200.) 14.14213)", True), + ("(< (square-root 200.) 14.14215)", True), + ("(= (square-root 200.) (sqrt 200.))", True), + ("""(define (sum-squares-range start end) + (define (sumsq-acc start end acc) + (if (> start end) acc (sumsq-acc (+ start 1) end (+ (* start start) acc)))) + (sumsq-acc start end 0))""", None), + ("(sum-squares-range 1 3000)", 9004500500), ## Tests tail recursion + ("(call/cc (lambda (throw) (+ 5 (* 10 (throw 1))))) ;; throw", 1), + ("(call/cc (lambda (throw) (+ 5 (* 10 1)))) ;; do not throw", 15), + ("""(call/cc (lambda (throw) + (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (escape 3)))))))) ; 1 level""", 35), + ("""(call/cc (lambda (throw) + (+ 5 (* 10 (call/cc (lambda (escape) (* 100 (throw 3)))))))) ; 2 levels""", 3), + ("""(call/cc (lambda (throw) + (+ 5 (* 10 (call/cc (lambda (escape) (* 100 1))))))) ; 0 levels""", 1005), + ("(* 1i 1i)", -1), ("(sqrt -1)", 1j), + ("(let ((a 1) (b 2)) (+ a b))", 3), + ("(let ((a 1) (b 2 3)) (+ a b))", SyntaxError), + ("(and 1 2 3)", 3), ("(and (> 2 1) 2 3)", 3), ("(and)", True), + ("(and (> 2 1) (> 2 3))", False), + ("(define-macro unless (lambda args `(if (not ,(car args)) (begin ,@(cdr args))))) ; test `", None), + ("(unless (= 2 (+ 1 1)) (display 2) 3 4)", None), + (r'(unless (= 4 (+ 1 1)) (display 2) (display "\n") 3 4)', 4), + ("(quote x)", 'x'), + ("(quote (1 2 three))", [1, 2, 'three']), + ("'x", 'x'), + ("'(one 2 3)", ['one', 2, 3]), + ("(define L (list 1 2 3))", None), + ("`(testing ,@L testing)", ['testing',1,2,3,'testing']), + ("`(testing ,L testing)", ['testing',[1,2,3],'testing']), + ("`,@L", SyntaxError), + ("""'(1 ;test comments ' + ;skip this line + 2 ; more ; comments ; ) ) + 3) ; final comment""", [1,2,3]), + ] + +def test(tests, name=''): + "For each (exp, expected) test case, see if eval(parse(exp)) == expected." + fails = 0 + for (x, expected) in tests: + try: + result = eval(parse(x)) + print(x, '=>', lispstr(result)) + ok = (result == expected) + except Exception as e: + print(x, '=raises=>', type(e).__name__, e) + ok = isinstance(expected, type) and issubclass(expected, Exception) and isinstance(e, expected) + if not ok: + fails += 1 + print('FAIL!!! Expected', expected) + print('%s %s: %d out of %d tests fail.' % ('*'*45, name, fails, len(tests))) + +if __name__ == '__main__': + from lis import * + test(lis_tests, 'lis.py') + from lispy import * + test(lis_tests+lispy_tests, 'lispy.py') + diff --git a/02-array-seq/lispy/examples_test.py b/02-array-seq/lispy/py3.10/examples_test.py similarity index 100% rename from 02-array-seq/lispy/examples_test.py rename to 02-array-seq/lispy/py3.10/examples_test.py diff --git a/02-array-seq/lispy/py3.10/lis.py b/02-array-seq/lispy/py3.10/lis.py new file mode 100644 index 0000000..035481f --- /dev/null +++ b/02-array-seq/lispy/py3.10/lis.py @@ -0,0 +1,188 @@ +################ Lispy: Scheme Interpreter in Python 3.10 + +## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html +## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) +## by Luciano Ramalho, adding type hints and pattern matching. + +################ Imports and Types + +import math +import operator as op +from collections import ChainMap +from collections.abc import MutableMapping, Iterator +from itertools import chain +from typing import Any, TypeAlias + +Symbol: TypeAlias = str +Atom: TypeAlias = float | int | Symbol +Expression: TypeAlias = Atom | list + +Environment: TypeAlias = MutableMapping[Symbol, object] + + +class Procedure: + "A user-defined Scheme procedure." + + def __init__(self, parms: list[Symbol], body: list[Expression], env: Environment): + self.parms = parms + self.body = body + self.env = env + + def __call__(self, *args: Expression) -> Any: + local_env = dict(zip(self.parms, args)) + env: Environment = ChainMap(local_env, self.env) + for exp in self.body: + result = evaluate(exp, env) + return result + + +################ Global Environment + +def standard_env() -> Environment: + "An environment with some Scheme standard procedures." + env: Environment = {} + env.update(vars(math)) # sin, cos, sqrt, pi, ... + env.update({ + '+': op.add, + '-': op.sub, + '*': op.mul, + '/': op.truediv, + '//': op.floordiv, + '>': op.gt, + '<': op.lt, + '>=': op.ge, + '<=': op.le, + '=': op.eq, + 'abs': abs, + 'append': lambda *args: list(chain(*args)), + 'apply': lambda proc, args: proc(*args), + 'begin': lambda *x: x[-1], + 'car': lambda x: x[0], + 'cdr': lambda x: x[1:], + 'cons': lambda x, y: [x] + y, + 'eq?': op.is_, + 'equal?': op.eq, + 'filter': lambda *args: list(filter(*args)), + 'length': len, + 'list': lambda *x: list(x), + 'list?': lambda x: isinstance(x, list), + 'map': lambda *args: list(map(*args)), + 'max': max, + 'min': min, + 'not': op.not_, + 'null?': lambda x: x == [], + 'number?': lambda x: isinstance(x, (int, float)), + 'procedure?': callable, + 'round': round, + 'symbol?': lambda x: isinstance(x, Symbol), + }) + return env + +################ Parsing: parse, tokenize, and read_from_tokens + +def parse(program: str) -> Expression: + "Read a Scheme expression from a string." + return read_from_tokens(tokenize(program)) + + +def tokenize(s: str) -> list[str]: + "Convert a string into a list of tokens." + return s.replace('(', ' ( ').replace(')', ' ) ').split() + + +def read_from_tokens(tokens: list[str]) -> Expression: + "Read an expression from a sequence of tokens." + if len(tokens) == 0: + raise SyntaxError('unexpected EOF while reading') + token = tokens.pop(0) + if '(' == token: + exp = [] + while tokens[0] != ')': + exp.append(read_from_tokens(tokens)) + tokens.pop(0) # discard ')' + return exp + elif ')' == token: + raise SyntaxError('unexpected )') + else: + return parse_atom(token) + + +def parse_atom(token: str) -> Atom: + "Numbers become numbers; every other token is a symbol." + try: + return int(token) + except ValueError: + try: + return float(token) + except ValueError: + return Symbol(token) + + +################ Interaction: A REPL + +def repl(prompt: str = 'lis.py> ') -> None: + "A prompt-read-evaluate-print loop." + global_env = standard_env() + while True: + val = evaluate(parse(input(prompt)), global_env) + if val is not None: + print(lispstr(val)) + + +def lispstr(exp: object) -> str: + "Convert a Python object back into a Lisp-readable string." + if isinstance(exp, list): + return '(' + ' '.join(map(lispstr, exp)) + ')' + else: + return str(exp) + + +################ eval + +# tag::EVALUATE[] +def evaluate(exp: Expression, env: Environment) -> Any: + "Evaluate an expression in an environment." + match exp: + case int(x) | float(x): + return x + case Symbol(var): + return env[var] + case []: + return [] + case ['quote', exp]: + return exp + case ['if', test, consequence, alternative]: + if evaluate(test, env): + return evaluate(consequence, env) + else: + return evaluate(alternative, env) + case ['define', Symbol(var), value_exp]: + env[var] = evaluate(value_exp, env) + case ['define', [Symbol(name), *parms], *body]: + env[name] = Procedure(parms, body, env) + case ['lambda', [*parms], *body]: + return Procedure(parms, body, env) + case [op, *args]: + proc = evaluate(op, env) + values = [evaluate(arg, env) for arg in args] + return proc(*values) + case _: + raise SyntaxError(repr(exp)) +# end::EVALUATE[] + + +################ non-interactive execution + + +def run_lines(source: str) -> Iterator[Any]: + global_env: Environment = standard_env() + tokens = tokenize(source) + while tokens: + exp = read_from_tokens(tokens) + yield evaluate(exp, global_env) + + +def run(source: str) -> Any: + for result in run_lines(source): + pass + return result diff --git a/02-array-seq/lispy/lis_test.py b/02-array-seq/lispy/py3.10/lis_test.py similarity index 97% rename from 02-array-seq/lispy/lis_test.py rename to 02-array-seq/lispy/py3.10/lis_test.py index e220e74..3688888 100644 --- a/02-array-seq/lispy/lis_test.py +++ b/02-array-seq/lispy/py3.10/lis_test.py @@ -12,7 +12,7 @@ ('(sum 1 2 3)', ['sum', 1, 2, 3]), ('(+ (* 2 100) (* 1 10))', ['+', ['*', 2, 100], ['*', 1, 10]]), ('99 100', 99), # parse stops at the first complete expression - ('(a)(b)', ['a']), + ('(a)(b)', ['a']), ]) def test_parse(source: str, expected: Expression) -> None: got = parse(source) @@ -122,7 +122,7 @@ def test_lambda(std_env: Environment) -> None: source = '(lambda (a b) (if (>= a b) a b))' func = evaluate(parse(source), std_env) assert func.parms == ['a', 'b'] - assert func.body == ['if', ['>=', 'a', 'b'], 'a', 'b'] + assert func.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] assert func.env is std_env assert func(1, 2) == 2 assert func(3, 2) == 3 @@ -176,7 +176,7 @@ def test_define_function(std_env: Environment) -> None: assert got is None max_fn = std_env['max'] assert max_fn.parms == ['a', 'b'] - assert max_fn.body == ['if', ['>=', 'a', 'b'], 'a', 'b'] + assert max_fn.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] assert max_fn.env is std_env assert max_fn(1, 2) == 2 assert max_fn(3, 2) == 3 diff --git a/02-array-seq/lispy/py3.10/meta_test.py b/02-array-seq/lispy/py3.10/meta_test.py new file mode 100644 index 0000000..3ddc3f3 --- /dev/null +++ b/02-array-seq/lispy/py3.10/meta_test.py @@ -0,0 +1,69 @@ +""" +Tests for developing a meta-circular interpreter, step-by-step. +""" + + +import operator as op + +from lis import run + +env_scm = """ +(define standard-env (list + (list (quote +) +) + (list (quote -) -) +)) +standard-env +""" + +def test_env_build(): + got = run(env_scm) + assert got == [['+', op.add], ['-', op.sub]] + +scan_scm = """ +(define l (quote (a b c))) +(define (scan what where) + (if (null? where) + () + (if (eq? what (car where)) + what + (scan what (cdr where)))) +) +""" + +def test_scan(): + source = scan_scm + '(scan (quote a) l )' + got = run(source) + assert got == 'a' + + +def test_scan_not_found(): + source = scan_scm + '(scan (quote z) l )' + got = run(source) + assert got == [] + + +lookup_scm = """ +(define env (list + (list (quote +) +) + (list (quote -) -) +)) +(define (lookup what where) + (if (null? where) + () + (if (eq? what (car (car where))) + (car (cdr (car where))) + (lookup what (cdr where)))) +) +""" + +def test_lookup(): + source = lookup_scm + '(lookup (quote +) env)' + got = run(source) + assert got == op.add + + +def test_lookup_not_found(): + source = lookup_scm + '(lookup (quote z) env )' + got = run(source) + assert got == [] + diff --git a/02-array-seq/lispy/py3.9-no-hints/README.md b/02-array-seq/lispy/py3.9-no-hints/README.md new file mode 100644 index 0000000..db6a11b --- /dev/null +++ b/02-array-seq/lispy/py3.9-no-hints/README.md @@ -0,0 +1,33 @@ +# Changes from the original + +While adapting Peter Norvig's [lis.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lis.py) for +use in _Fluent Python, Second Edition_, I made a few changes for didactic reasons. + +_Luciano Ramalho_ + +## Major changes + +* Make the `lambda` form accept more than one expression as the body. This is consistent with _Scheme_ syntax, and provides a useful example for the book. To implement this: + * In `Procedure.__call__`: evaluate `self.body` as a list of expressions, instead of a single expression. Return the value of the last expression. + * In `evaluate()`: when processing `lambda`, unpack expression into `(_, parms, *body)`, to accept a list of expressions as the body. +* Remove the `global_env` global `dict`. It is only used as a default value for the `env` parameter in `evaluate()`, but it is unsafe to use mutable data structures as parameter default values. To implement this: + * In `repl()`: create local variable `global_env` and pass it as the `env` paramater of `evaluate()`. + * In `evaluate()`, remove `global_env` default value for `env`. +* Rewrite the custom test script +[lispytest.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispytest.py) as +[lis_test.py](https://github.com/fluentpython/example-code-2e/blob/master/02-array-seq/lispy/py3.9/lis_test.py): +a standard [pytest](https://docs.pytest.org) test suite including new test cases, preserving all Norvig's the test cases for +[lis.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lis.py) +but removing the test cases for the features implemented only in +[lispy.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispy.py) + + +## Minor changes + +Cosmetic changes to make the code look more familiar to +Python programmers, the audience of _Fluent Python_. + +* Rename `eval()` to `evaluate()`, to avoid confusion with Python's `eval` built-in function. +* Refer to the list class as `list` instead of aliasing as `List`, to avoid confusion with `typing.List` which is often imported as `List`. +* Import `collections.ChainMap` as `ChainMap` instead of `Environment`. + diff --git a/02-array-seq/lispy/py3.9-no-hints/lis.py b/02-array-seq/lispy/py3.9-no-hints/lis.py new file mode 100644 index 0000000..c74c3ae --- /dev/null +++ b/02-array-seq/lispy/py3.9-no-hints/lis.py @@ -0,0 +1,142 @@ +################ Lispy: Scheme Interpreter in Python 3.9 + +## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html +## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) +## by Luciano Ramalho, adding type hints and pattern matching. + +################ Imports and Types + +import math +import operator as op +from collections import ChainMap +from typing import Any + +Symbol = str # A Lisp Symbol is implemented as a Python str +Number = (int, float) # A Lisp Number is implemented as a Python int or float + +class Procedure: + "A user-defined Scheme procedure." + def __init__(self, parms, body, env): + self.parms, self.body, self.env = parms, body, env + def __call__(self, *args): + env = ChainMap(dict(zip(self.parms, args)), self.env) + for exp in self.body: + result = evaluate(exp, env) + return result + + +################ Global Environment + +def standard_env(): + "An environment with some Scheme standard procedures." + env = {} + env.update(vars(math)) # sin, cos, sqrt, pi, ... + env.update({ + '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, + '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, + 'abs': abs, + 'append': op.add, + 'apply': lambda proc, args: proc(*args), + 'begin': lambda *x: x[-1], + 'car': lambda x: x[0], + 'cdr': lambda x: x[1:], + 'cons': lambda x,y: [x] + y, + 'eq?': op.is_, + 'equal?': op.eq, + 'length': len, + 'list': lambda *x: list(x), + 'list?': lambda x: isinstance(x,list), + 'map': lambda *args: list(map(*args)), + 'max': max, + 'min': min, + 'not': op.not_, + 'null?': lambda x: x == [], + 'number?': lambda x: isinstance(x, Number), + 'procedure?': callable, + 'round': round, + 'symbol?': lambda x: isinstance(x, Symbol), + }) + return env + +################ Parsing: parse, tokenize, and read_from_tokens + +def parse(program): + "Read a Scheme expression from a string." + return read_from_tokens(tokenize(program)) + +def tokenize(s): + "Convert a string into a list of tokens." + return s.replace('(',' ( ').replace(')',' ) ').split() + +def read_from_tokens(tokens): + "Read an expression from a sequence of tokens." + if len(tokens) == 0: + raise SyntaxError('unexpected EOF while reading') + token = tokens.pop(0) + if '(' == token: + exp = [] + while tokens[0] != ')': + exp.append(read_from_tokens(tokens)) + tokens.pop(0) # discard ')' + return exp + elif ')' == token: + raise SyntaxError('unexpected )') + else: + return parse_atom(token) + + +def parse_atom(token: str): + "Numbers become numbers; every other token is a symbol." + try: + return int(token) + except ValueError: + try: + return float(token) + except ValueError: + return Symbol(token) + + +################ Interaction: A REPL + +def repl(prompt: str = 'lis.py> ') -> None: + "A prompt-read-eval-print loop." + global_env = standard_env() + while True: + val = evaluate(parse(input(prompt)), global_env) + if val is not None: + print(lispstr(val)) + + +def lispstr(exp): + "Convert a Python object back into a Lisp-readable string." + if isinstance(exp, list): + return '(' + ' '.join(map(lispstr, exp)) + ')' + else: + return str(exp) + + +################ eval + +def evaluate(x, env): + "Evaluate an expression in an environment." + if isinstance(x, Symbol): # variable reference + return env[x] + elif not isinstance(x, list): # constant literal + return x + elif x[0] == 'quote': # (quote exp) + (_, exp) = x + return exp + elif x[0] == 'if': # (if test conseq alt) + (_, test, conseq, alt) = x + exp = (conseq if evaluate(test, env) else alt) + return evaluate(exp, env) + elif x[0] == 'define': # (define var exp) + (_, var, exp) = x + env[var] = evaluate(exp, env) + elif x[0] == 'lambda': # (lambda (var...) body) + (_, parms, *body) = x + return Procedure(parms, body, env) + else: # (proc arg...) + proc = evaluate(x[0], env) + args = [evaluate(exp, env) for exp in x[1:]] + return proc(*args) diff --git a/02-array-seq/lispy/py3.9-no-hints/lis_test.py b/02-array-seq/lispy/py3.9-no-hints/lis_test.py new file mode 100644 index 0000000..f5f4f8e --- /dev/null +++ b/02-array-seq/lispy/py3.9-no-hints/lis_test.py @@ -0,0 +1,166 @@ +from pytest import mark, fixture + +from lis import parse, evaluate, standard_env + +############################################################# tests for parse + +@mark.parametrize( 'source, expected', [ + ('7', 7), + ('x', 'x'), + ('(sum 1 2 3)', ['sum', 1, 2, 3]), + ('(+ (* 2 100) (* 1 10))', ['+', ['*', 2, 100], ['*', 1, 10]]), + ('99 100', 99), # parse stops at the first complete expression + ('(a)(b)', ['a']), +]) +def test_parse(source: str, expected): + got = parse(source) + assert got == expected + + +########################################################## tests for evaluate + +# Norvig's tests are not isolated: they assume the +# same environment from first to last test. +global_env_for_first_test = standard_env() + +@mark.parametrize( 'source, expected', [ + ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), + ("(+ 2 2)", 4), + ("(+ (* 2 100) (* 1 10))", 210), + ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), + ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), + ("(define x 3)", None), + ("x", 3), + ("(+ x x)", 6), + ("((lambda (x) (+ x x)) 5)", 10), + ("(define twice (lambda (x) (* 2 x)))", None), + ("(twice 5)", 10), + ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), + ("((compose list twice) 5)", [10]), + ("(define repeat (lambda (f) (compose f f)))", None), + ("((repeat twice) 5)", 20), + ("((repeat (repeat twice)) 5)", 80), + ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), + ("(fact 3)", 6), + ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), + ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), + ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), + ("""(define combine (lambda (f) + (lambda (x y) + (if (null? x) (quote ()) + (f (list (car x) (car y)) + ((combine f) (cdr x) (cdr y)))))))""", None), + ("(define zip (combine cons))", None), + ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), + ("""(define riff-shuffle (lambda (deck) + (begin + (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) + (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) + (define mid (lambda (seq) (/ (length seq) 2))) + ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), + ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), + ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), + ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), +]) +def test_evaluate(source, expected): + got = evaluate(parse(source), global_env_for_first_test) + assert got == expected + + +@fixture +def std_env(): + return standard_env() + +# tests for each of the cases in evaluate + +def test_evaluate_variable(): + env = dict(x=10) + source = 'x' + expected = 10 + got = evaluate(parse(source), env) + assert got == expected + + +def test_evaluate_literal(std_env): + source = '3.3' + expected = 3.3 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_quote(std_env): + source = '(quote (1.1 is not 1))' + expected = [1.1, 'is', 'not', 1] + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_if_true(std_env) -> None: + source = '(if 1 10 no-such-thing)' + expected = 10 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_if_false(std_env) -> None: + source = '(if 0 no-such-thing 20)' + expected = 20 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_define(std_env) -> None: + source = '(define answer (* 6 7))' + got = evaluate(parse(source), std_env) + assert got is None + assert std_env['answer'] == 42 + + +def test_lambda(std_env) -> None: + source = '(lambda (a b) (if (>= a b) a b))' + func = evaluate(parse(source), std_env) + assert func.parms == ['a', 'b'] + assert func.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] + assert func.env is std_env + assert func(1, 2) == 2 + assert func(3, 2) == 3 + + +def test_begin(std_env) -> None: + source = """ + (begin + (define x (* 2 3)) + (* x 7) + ) + """ + got = evaluate(parse(source), std_env) + assert got == 42 + + +def test_invocation_builtin_car(std_env) -> None: + source = '(car (quote (11 22 33)))' + got = evaluate(parse(source), std_env) + assert got == 11 + + +def test_invocation_builtin_append(std_env) -> None: + source = '(append (quote (a b)) (quote (c d)))' + got = evaluate(parse(source), std_env) + assert got == ['a', 'b', 'c', 'd'] + + +def test_invocation_builtin_map(std_env) -> None: + source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))' + got = evaluate(parse(source), std_env) + assert got == [2, 4, 6] + + +def test_invocation_user_procedure(std_env): + source = """ + (begin + (define max (lambda (a b) (if (>= a b) a b))) + (max 22 11) + ) + """ + got = evaluate(parse(source), std_env) + assert got == 22 diff --git a/02-array-seq/lispy/py3.9/README.md b/02-array-seq/lispy/py3.9/README.md new file mode 100644 index 0000000..db6a11b --- /dev/null +++ b/02-array-seq/lispy/py3.9/README.md @@ -0,0 +1,33 @@ +# Changes from the original + +While adapting Peter Norvig's [lis.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lis.py) for +use in _Fluent Python, Second Edition_, I made a few changes for didactic reasons. + +_Luciano Ramalho_ + +## Major changes + +* Make the `lambda` form accept more than one expression as the body. This is consistent with _Scheme_ syntax, and provides a useful example for the book. To implement this: + * In `Procedure.__call__`: evaluate `self.body` as a list of expressions, instead of a single expression. Return the value of the last expression. + * In `evaluate()`: when processing `lambda`, unpack expression into `(_, parms, *body)`, to accept a list of expressions as the body. +* Remove the `global_env` global `dict`. It is only used as a default value for the `env` parameter in `evaluate()`, but it is unsafe to use mutable data structures as parameter default values. To implement this: + * In `repl()`: create local variable `global_env` and pass it as the `env` paramater of `evaluate()`. + * In `evaluate()`, remove `global_env` default value for `env`. +* Rewrite the custom test script +[lispytest.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispytest.py) as +[lis_test.py](https://github.com/fluentpython/example-code-2e/blob/master/02-array-seq/lispy/py3.9/lis_test.py): +a standard [pytest](https://docs.pytest.org) test suite including new test cases, preserving all Norvig's the test cases for +[lis.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lis.py) +but removing the test cases for the features implemented only in +[lispy.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispy.py) + + +## Minor changes + +Cosmetic changes to make the code look more familiar to +Python programmers, the audience of _Fluent Python_. + +* Rename `eval()` to `evaluate()`, to avoid confusion with Python's `eval` built-in function. +* Refer to the list class as `list` instead of aliasing as `List`, to avoid confusion with `typing.List` which is often imported as `List`. +* Import `collections.ChainMap` as `ChainMap` instead of `Environment`. + diff --git a/02-array-seq/lispy/py3.9/lis.py b/02-array-seq/lispy/py3.9/lis.py new file mode 100644 index 0000000..11f4402 --- /dev/null +++ b/02-array-seq/lispy/py3.9/lis.py @@ -0,0 +1,155 @@ +################ Lispy: Scheme Interpreter in Python 3.9 + +## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html +## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) +## by Luciano Ramalho, adding type hints and pattern matching. + +################ Imports and Types + +import math +import operator as op +from collections import ChainMap +from collections.abc import MutableMapping, Iterator +from itertools import chain +from typing import Any, Union + +Symbol = str +Atom = Union[float, int, Symbol] +Expression = Union[Atom, list] + +Environment = MutableMapping[Symbol, object] + + +class Procedure: + "A user-defined Scheme procedure." + + def __init__(self, parms: list[Symbol], body: list[Expression], env: Environment): + self.parms = parms + self.body = body + self.env = env + + def __call__(self, *args: Expression) -> Any: + local_env = dict(zip(self.parms, args)) + env: Environment = ChainMap(local_env, self.env) + for exp in self.body: + result = evaluate(exp, env) + return result + + +################ Global Environment + +def standard_env() -> Environment: + "An environment with some Scheme standard procedures." + env: Environment = {} + env.update(vars(math)) # sin, cos, sqrt, pi, ... + env.update({ + '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, + '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, + 'abs': abs, + 'append': op.add, + 'apply': lambda proc, args: proc(*args), + 'begin': lambda *x: x[-1], + 'car': lambda x: x[0], + 'cdr': lambda x: x[1:], + 'cons': lambda x,y: [x] + y, + 'eq?': op.is_, + 'equal?': op.eq, + 'length': len, + 'list': lambda *x: list(x), + 'list?': lambda x: isinstance(x,list), + 'map': lambda *args: list(map(*args)), + 'max': max, + 'min': min, + 'not': op.not_, + 'null?': lambda x: x == [], + 'number?': lambda x: isinstance(x, (int, float)), + 'procedure?': callable, + 'round': round, + 'symbol?': lambda x: isinstance(x, Symbol), + }) + return env + +################ Parsing: parse, tokenize, and read_from_tokens + +def parse(program: str) -> Expression: + "Read a Scheme expression from a string." + return read_from_tokens(tokenize(program)) + + +def tokenize(s: str) -> list[str]: + "Convert a string into a list of tokens." + return s.replace('(', ' ( ').replace(')', ' ) ').split() + + +def read_from_tokens(tokens: list[str]) -> Expression: + "Read an expression from a sequence of tokens." + if len(tokens) == 0: + raise SyntaxError('unexpected EOF while reading') + token = tokens.pop(0) + if '(' == token: + exp = [] + while tokens[0] != ')': + exp.append(read_from_tokens(tokens)) + tokens.pop(0) # discard ')' + return exp + elif ')' == token: + raise SyntaxError('unexpected )') + else: + return parse_atom(token) + + +def parse_atom(token: str) -> Atom: + "Numbers become numbers; every other token is a symbol." + try: + return int(token) + except ValueError: + try: + return float(token) + except ValueError: + return Symbol(token) + + +################ Interaction: A REPL + +def repl(prompt: str = 'lis.py> ') -> None: + "A prompt-read-eval-print loop." + global_env = standard_env() + while True: + val = evaluate(parse(input(prompt)), global_env) + if val is not None: + print(lispstr(val)) + + +def lispstr(exp: object) -> str: + "Convert a Python object back into a Lisp-readable string." + if isinstance(exp, list): + return '(' + ' '.join(map(lispstr, exp)) + ')' + else: + return str(exp) + + +################ eval + +def evaluate(x: Expression, env: Environment) -> Any: + "Evaluate an expression in an environment." + if isinstance(x, Symbol): # variable reference + return env[x] + elif not isinstance(x, list): # constant literal + return x + elif x[0] == 'quote': # (quote exp) + (_, exp) = x + return exp + elif x[0] == 'if': # (if test conseq alt) + (_, test, conseq, alt) = x + exp = (conseq if evaluate(test, env) else alt) + return evaluate(exp, env) + elif x[0] == 'define': # (define var exp) + (_, var, exp) = x + env[var] = evaluate(exp, env) + elif x[0] == 'lambda': # (lambda (var...) body) + (_, parms, *body) = x + return Procedure(parms, body, env) + else: # (proc arg...) + proc = evaluate(x[0], env) + args = [evaluate(exp, env) for exp in x[1:]] + return proc(*args) diff --git a/02-array-seq/lispy/py3.9/lis_test.py b/02-array-seq/lispy/py3.9/lis_test.py new file mode 100644 index 0000000..106ac24 --- /dev/null +++ b/02-array-seq/lispy/py3.9/lis_test.py @@ -0,0 +1,168 @@ +from typing import Any, Optional + +from pytest import mark, fixture + +from lis import parse, evaluate, standard_env, Symbol, Environment, Expression + +############################################################# tests for parse + +@mark.parametrize( 'source, expected', [ + ('7', 7), + ('x', 'x'), + ('(sum 1 2 3)', ['sum', 1, 2, 3]), + ('(+ (* 2 100) (* 1 10))', ['+', ['*', 2, 100], ['*', 1, 10]]), + ('99 100', 99), # parse stops at the first complete expression + ('(a)(b)', ['a']), +]) +def test_parse(source: str, expected: Expression) -> None: + got = parse(source) + assert got == expected + + +########################################################## tests for evaluate + +# Norvig's tests are not isolated: they assume the +# same environment from first to last test. +global_env_for_first_test = standard_env() + +@mark.parametrize( 'source, expected', [ + ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), + ("(+ 2 2)", 4), + ("(+ (* 2 100) (* 1 10))", 210), + ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), + ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), + ("(define x 3)", None), + ("x", 3), + ("(+ x x)", 6), + ("((lambda (x) (+ x x)) 5)", 10), + ("(define twice (lambda (x) (* 2 x)))", None), + ("(twice 5)", 10), + ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), + ("((compose list twice) 5)", [10]), + ("(define repeat (lambda (f) (compose f f)))", None), + ("((repeat twice) 5)", 20), + ("((repeat (repeat twice)) 5)", 80), + ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), + ("(fact 3)", 6), + ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), + ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), + ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), + ("""(define combine (lambda (f) + (lambda (x y) + (if (null? x) (quote ()) + (f (list (car x) (car y)) + ((combine f) (cdr x) (cdr y)))))))""", None), + ("(define zip (combine cons))", None), + ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), + ("""(define riff-shuffle (lambda (deck) + (begin + (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) + (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) + (define mid (lambda (seq) (/ (length seq) 2))) + ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), + ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), + ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), + ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), +]) +def test_evaluate(source: str, expected: Optional[Expression]) -> None: + got = evaluate(parse(source), global_env_for_first_test) + assert got == expected + + +@fixture +def std_env() -> Environment: + return standard_env() + +# tests for each of the cases in evaluate + +def test_evaluate_variable() -> None: + env: Environment = dict(x=10) + source = 'x' + expected = 10 + got = evaluate(parse(source), env) + assert got == expected + + +def test_evaluate_literal(std_env: Environment) -> None: + source = '3.3' + expected = 3.3 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_quote(std_env: Environment) -> None: + source = '(quote (1.1 is not 1))' + expected = [1.1, 'is', 'not', 1] + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_if_true(std_env: Environment) -> None: + source = '(if 1 10 no-such-thing)' + expected = 10 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_if_false(std_env: Environment) -> None: + source = '(if 0 no-such-thing 20)' + expected = 20 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_define(std_env: Environment) -> None: + source = '(define answer (* 6 7))' + got = evaluate(parse(source), std_env) + assert got is None + assert std_env['answer'] == 42 + + +def test_lambda(std_env: Environment) -> None: + source = '(lambda (a b) (if (>= a b) a b))' + func = evaluate(parse(source), std_env) + assert func.parms == ['a', 'b'] + assert func.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] + assert func.env is std_env + assert func(1, 2) == 2 + assert func(3, 2) == 3 + + +def test_begin(std_env: Environment) -> None: + source = """ + (begin + (define x (* 2 3)) + (* x 7) + ) + """ + got = evaluate(parse(source), std_env) + assert got == 42 + + +def test_invocation_builtin_car(std_env: Environment) -> None: + source = '(car (quote (11 22 33)))' + got = evaluate(parse(source), std_env) + assert got == 11 + + +def test_invocation_builtin_append(std_env: Environment) -> None: + source = '(append (quote (a b)) (quote (c d)))' + got = evaluate(parse(source), std_env) + assert got == ['a', 'b', 'c', 'd'] + + +def test_invocation_builtin_map(std_env: Environment) -> None: + source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))' + got = evaluate(parse(source), std_env) + assert got == [2, 4, 6] + + +def test_invocation_user_procedure(std_env: Environment) -> None: + source = """ + (begin + (define max (lambda (a b) (if (>= a b) a b))) + (max 22 11) + ) + """ + got = evaluate(parse(source), std_env) + assert got == 22 diff --git a/02-array-seq/match_lat_lon.py b/02-array-seq/match_lat_lon.py index 592aa70..20c7131 100644 --- a/02-array-seq/match_lat_lon.py +++ b/02-array-seq/match_lat_lon.py @@ -7,7 +7,7 @@ | latitude | longitude Mexico City | 19.4333 | -99.1333 New York-Newark | 40.8086 | -74.0204 - Sao Paulo | -23.5478 | -46.6358 + São Paulo | -23.5478 | -46.6358 """ @@ -17,7 +17,7 @@ ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)), ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)), ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)), - ('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)), + ('São Paulo', 'BR', 19.649, (-23.547778, -46.635833)), ] def main(): diff --git a/02-array-seq/metro_lat_lon.py b/02-array-seq/metro_lat_lon.py index 930a993..899bcb8 100644 --- a/02-array-seq/metro_lat_lon.py +++ b/02-array-seq/metro_lat_lon.py @@ -1,5 +1,5 @@ """ -metro_lat_long.py +metro_lat_lon.py Demonstration of nested tuple unpacking:: @@ -7,16 +7,17 @@ | latitude | longitude Mexico City | 19.4333 | -99.1333 New York-Newark | 40.8086 | -74.0204 - Sao Paulo | -23.5478 | -46.6358 + São Paulo | -23.5478 | -46.6358 """ +# tag::MAIN[] metro_areas = [ ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)), # <1> ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)), ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)), ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)), - ('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)), + ('São Paulo', 'BR', 19.649, (-23.547778, -46.635833)), ] def main(): @@ -27,3 +28,4 @@ def main(): if __name__ == '__main__': main() +# end::MAIN[] \ No newline at end of file diff --git a/03-dict-set/py3.10/creator.py b/03-dict-set/py3.10/creator.py new file mode 100644 index 0000000..97d6bde --- /dev/null +++ b/03-dict-set/py3.10/creator.py @@ -0,0 +1,40 @@ +""" +Pattern matching with mapping—requires Python ≥ 3.10 + +# tag::DICT_MATCH_TEST[] +>>> b1 = dict(api=1, author='Douglas Hofstadter', +... type='book', title='Gödel, Escher, Bach') +>>> get_creators(b1) +['Douglas Hofstadter'] +>>> from collections import OrderedDict +>>> b2 = OrderedDict(api=2, type='book', +... title='Python in a Nutshell', +... authors='Martelli Ravenscroft Holden'.split()) +>>> get_creators(b2) +['Martelli', 'Ravenscroft', 'Holden'] +>>> get_creators({'type': 'book', 'pages': 770}) +Traceback (most recent call last): + ... +ValueError: Invalid 'book' record: {'type': 'book', 'pages': 770} +>>> get_creators('Spam, spam, spam') +Traceback (most recent call last): + ... +ValueError: Invalid record: 'Spam, spam, spam' + +# end::DICT_MATCH_TEST[] +""" + +# tag::DICT_MATCH[] +def get_creators(record: dict) -> list: + match record: + case {'type': 'book', 'api': 2, 'authors': [*names]}: # <1> + return names + case {'type': 'book', 'api': 1, 'author': name}: # <2> + return [name] + case {'type': 'book'}: # <3> + raise ValueError(f"Invalid 'book' record: {record!r}") + case {'type': 'movie', 'director': name}: # <4> + return [name] + case _: # <5> + raise ValueError(f'Invalid record: {record!r}') +# end::DICT_MATCH[] diff --git a/04-text-byte/categories.py b/04-text-byte/categories.py index 11cc28e..ae8dedf 100644 --- a/04-text-byte/categories.py +++ b/04-text-byte/categories.py @@ -34,11 +34,11 @@ def main(args): print(count, 'characters shown') else: counts, firsts = category_stats() - for cat, count in counts.most_common(): + for i, (cat, count) in enumerate(counts.most_common(), 1): first = firsts[cat] if cat == 'Cs': first = f'(surrogate U+{ord(first):04X})' - print(f'{count:6} {cat} {first}') + print(f'{i:2} {count:6} {cat} {first}') if __name__ == '__main__': diff --git a/04-text-byte/stdout_check.py b/04-text-byte/stdout_check.py index dcef224..c20139f 100644 --- a/04-text-byte/stdout_check.py +++ b/04-text-byte/stdout_check.py @@ -8,9 +8,9 @@ print() test_chars = [ - '\u2026', # HORIZONTAL ELLIPSIS (in cp1252) - '\u221E', # INFINITY (in cp437) - '\u32B7', # CIRCLED NUMBER FORTY TWO + '\N{HORIZONTAL ELLIPSIS}', # exists in cp1252, not in cp437 + '\N{INFINITY}', # exists in cp437, not in cp1252 + '\N{CIRCLED NUMBER FORTY TWO}', # not in cp437 or in cp1252 ] for char in test_chars: diff --git a/05-record-like/README.asciidoc b/05-data-classes/README.asciidoc similarity index 100% rename from 05-record-like/README.asciidoc rename to 05-data-classes/README.asciidoc diff --git a/05-record-like/cards.doctest b/05-data-classes/cards.doctest similarity index 100% rename from 05-record-like/cards.doctest rename to 05-data-classes/cards.doctest diff --git a/05-record-like/cards.py b/05-data-classes/cards.py similarity index 100% rename from 05-record-like/cards.py rename to 05-data-classes/cards.py diff --git a/05-record-like/cards_enum.py b/05-data-classes/cards_enum.py similarity index 100% rename from 05-record-like/cards_enum.py rename to 05-data-classes/cards_enum.py diff --git a/05-record-like/class/coordinates.py b/05-data-classes/class/coordinates.py similarity index 94% rename from 05-record-like/class/coordinates.py rename to 05-data-classes/class/coordinates.py index 683a97a..84dcd1d 100644 --- a/05-record-like/class/coordinates.py +++ b/05-data-classes/class/coordinates.py @@ -13,4 +13,4 @@ def __init__(self, lat, lon): self.lat = lat self.lon = lon -# end::COORDINATE[] +# end::COORDINATE[] \ No newline at end of file diff --git a/05-record-like/dataclass/club.py b/05-data-classes/dataclass/club.py similarity index 100% rename from 05-record-like/dataclass/club.py rename to 05-data-classes/dataclass/club.py diff --git a/05-record-like/dataclass/club_generic.py b/05-data-classes/dataclass/club_generic.py similarity index 100% rename from 05-record-like/dataclass/club_generic.py rename to 05-data-classes/dataclass/club_generic.py diff --git a/05-record-like/dataclass/club_wrong.py b/05-data-classes/dataclass/club_wrong.py similarity index 100% rename from 05-record-like/dataclass/club_wrong.py rename to 05-data-classes/dataclass/club_wrong.py diff --git a/05-record-like/dataclass/coordinates.py b/05-data-classes/dataclass/coordinates.py similarity index 100% rename from 05-record-like/dataclass/coordinates.py rename to 05-data-classes/dataclass/coordinates.py diff --git a/05-record-like/dataclass/hackerclub.py b/05-data-classes/dataclass/hackerclub.py similarity index 100% rename from 05-record-like/dataclass/hackerclub.py rename to 05-data-classes/dataclass/hackerclub.py diff --git a/05-record-like/dataclass/hackerclub_annotated.py b/05-data-classes/dataclass/hackerclub_annotated.py similarity index 100% rename from 05-record-like/dataclass/hackerclub_annotated.py rename to 05-data-classes/dataclass/hackerclub_annotated.py diff --git a/05-record-like/dataclass/resource.py b/05-data-classes/dataclass/resource.py similarity index 100% rename from 05-record-like/dataclass/resource.py rename to 05-data-classes/dataclass/resource.py diff --git a/05-record-like/dataclass/resource_repr.py b/05-data-classes/dataclass/resource_repr.py similarity index 100% rename from 05-record-like/dataclass/resource_repr.py rename to 05-data-classes/dataclass/resource_repr.py diff --git a/05-record-like/frenchdeck.doctest b/05-data-classes/frenchdeck.doctest similarity index 100% rename from 05-record-like/frenchdeck.doctest rename to 05-data-classes/frenchdeck.doctest diff --git a/05-record-like/frenchdeck.py b/05-data-classes/frenchdeck.py similarity index 100% rename from 05-record-like/frenchdeck.py rename to 05-data-classes/frenchdeck.py diff --git a/05-data-classes/match_cities.py b/05-data-classes/match_cities.py new file mode 100644 index 0000000..e795c50 --- /dev/null +++ b/05-data-classes/match_cities.py @@ -0,0 +1,92 @@ +""" +match_cities.py +""" + +# tag::CITY[] +import typing + +class City(typing.NamedTuple): + continent: str + name: str + country: str + + +cities = [ + City('Asia', 'Tokyo', 'JP'), + City('Asia', 'Delhi', 'IN'), + City('North America', 'Mexico City', 'MX'), + City('North America', 'New York', 'US'), + City('South America', 'São Paulo', 'BR'), +] +# end::CITY[] + +# tag::ASIA[] +def match_asian_cities(): + results = [] + for city in cities: + match city: + case City(continent='Asia'): + results.append(city) + return results +# end::ASIA[] + +# tag::ASIA_POSITIONAL[] +def match_asian_cities_pos(): + results = [] + for city in cities: + match city: + case City('Asia'): + results.append(city) + return results +# end::ASIA_POSITIONAL[] + + +# tag::ASIA_COUNTRIES[] +def match_asian_countries(): + results = [] + for city in cities: + match city: + case City(continent='Asia', country=cc): + results.append(cc) + return results +# end::ASIA_COUNTRIES[] + +# tag::ASIA_COUNTRIES_POSITIONAL[] +def match_asian_countries_pos(): + results = [] + for city in cities: + match city: + case City('Asia', _, country): + results.append(country) + return results +# end::ASIA_COUNTRIES_POSITIONAL[] + + +def match_india(): + results = [] + for city in cities: + match city: + case City(_, name, 'IN'): + results.append(name) + return results + + +def match_brazil(): + results = [] + for city in cities: + match city: + case City(country='BR', name=name): + results.append(name) + return results + + + +def main(): + tests = ((n, f) for n, f in globals().items() if n.startswith('match_')) + + for name, func in tests: + print(f'{name:15}\t{func()}') + + +if __name__ == '__main__': + main() diff --git a/05-record-like/meaning/demo_dc.py b/05-data-classes/meaning/demo_dc.py similarity index 100% rename from 05-record-like/meaning/demo_dc.py rename to 05-data-classes/meaning/demo_dc.py diff --git a/05-record-like/meaning/demo_nt.py b/05-data-classes/meaning/demo_nt.py similarity index 100% rename from 05-record-like/meaning/demo_nt.py rename to 05-data-classes/meaning/demo_nt.py diff --git a/05-record-like/meaning/demo_plain.py b/05-data-classes/meaning/demo_plain.py similarity index 100% rename from 05-record-like/meaning/demo_plain.py rename to 05-data-classes/meaning/demo_plain.py diff --git a/05-record-like/typing_namedtuple/coordinates.py b/05-data-classes/typing_namedtuple/coordinates.py similarity index 100% rename from 05-record-like/typing_namedtuple/coordinates.py rename to 05-data-classes/typing_namedtuple/coordinates.py diff --git a/05-record-like/typing_namedtuple/coordinates2.py b/05-data-classes/typing_namedtuple/coordinates2.py similarity index 95% rename from 05-record-like/typing_namedtuple/coordinates2.py rename to 05-data-classes/typing_namedtuple/coordinates2.py index e523a11..2032311 100644 --- a/05-record-like/typing_namedtuple/coordinates2.py +++ b/05-data-classes/typing_namedtuple/coordinates2.py @@ -17,4 +17,4 @@ class Coordinate(NamedTuple): lat: float # <1> lon: float reference: str = 'WGS84' # <2> -# end::COORDINATE[] +# end::COORDINATE[] \ No newline at end of file diff --git a/05-record-like/typing_namedtuple/nocheck_demo.py b/05-data-classes/typing_namedtuple/nocheck_demo.py similarity index 71% rename from 05-record-like/typing_namedtuple/nocheck_demo.py rename to 05-data-classes/typing_namedtuple/nocheck_demo.py index 57e8fc8..8ca5dc1 100644 --- a/05-record-like/typing_namedtuple/nocheck_demo.py +++ b/05-data-classes/typing_namedtuple/nocheck_demo.py @@ -5,5 +5,5 @@ class Coordinate(typing.NamedTuple): lat: float lon: float -trash = Coordinate('foo', None) # <1> +trash = Coordinate('Ni!', None) # <1> print(trash) diff --git a/05-record-like/struct/README b/05-record-like/struct/README deleted file mode 100644 index 65e16f2..0000000 --- a/05-record-like/struct/README +++ /dev/null @@ -1,17 +0,0 @@ -To compile `metro_write.c` on MacOS 10.14 with XCode: - -$ clang metro_write.c -o metro - -Output: - -$ xxd metro_areas.bin -00000000: e207 0000 546f 6b79 6f00 0000 0000 0000 ....Tokyo....... -00000010: 0000 0000 0000 0000 4a50 01e7 1158 274c ........JP...X'L -00000020: df07 0000 5368 616e 6768 6169 0000 0000 ....Shanghai.... -00000030: 0100 0000 0e00 0000 434e 0000 f8a0 134c ........CN.....L -00000040: df07 0000 4a61 6b61 7274 6100 0000 0000 ....Jakarta..... -00000050: 0000 0000 0000 0000 4944 0000 bcc5 f14b ........ID.....K -$ python3 metro_read.py -2018 Tokyo, JP 43,868,228 -2015 Shanghai, CN 38,700,000 -2015 Jakarta, ID 31,689,592 diff --git a/05-record-like/struct/metro b/05-record-like/struct/metro deleted file mode 100755 index 69a29133a0771710a7d1fb6b0ca83b2187a0261c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8764 zcmeHNU1(fI7@chrZS2n`C=&l*TaucVl0>l(iwQ~1r8n(1i6*H*s*{_|Cc9>{8}4pM z@=!<#3ogq--t@7L1q%lBp+c)|tx3^`qE8j7hG1n27Fy~eCl(h*PC-BPB)^Qls-o3CpRUIyJOc2dOErEr49 zawugoH9M9pHo5s8?6vqB?Fa`u19sYcrToYzBjMOg*hG+Zp)PgnvF?BE6mi$7A zc(U}uZoW~QZ?{cAT)|f)wj3!#$&?w5PulHnzL?F|Y4;PCo10t8+4x*EJ}#s2i39@O zd>3s#t|1z+o5Sj{o?IvG5J<|N?xF6o9;^p7rOrBW)m$f7!>VwUl<}BME=-RlVsa*B zZp-J;aJ_N7W1LTCPT3~qWTG?+Tl1^Kc;>g0!7ySspVP;FDPzXJ<=0^IwJ{h*T+YY$ ztyMkNqx3xu_V%6BdU~DjSiq$lFzpR{63C-;Rjoqo$KL_#`&e}WM^Ilt z%{bCDpsWKwh5GV75TM?LGJsNUjAbd;p}e2|BF4LxoC?9$Py|r8&Rb`bW@{`u);hir z8waMnfWkcbLP!7n`QR%XS2{n=epDOjcol5{6d#J%iwsfdD=n+8`wqc}v_)%13gqG6 zjdJ7XQU~_&9ej^}ttM+R7vwXzkW7WATaL9Lujm+pU-5k_dy%41AFEv;3?oK z;3?oK;3@F`RA5-o+||$hzJly8agUI>uIEmz1hX}tVV7_!D}otqb4|O*m&=^Jpl4>! zuW2hA*mdbk*Q)i*Ej@SSF7_k&5Af*zf($*GZy)*VYQ96ms&@6ns&;MQk-uKMv8LVp zh2ubvu??m1pCD4tXdmVexH*;-$L%*)wIBP)ac50iFXLz{am-j8i}?*?z>n`yLm%|6 zY7cuj^bbAPG033YkuxebJzppk@?RkwpS`RvZ3=%nK$Pp~M8Jy3e+qQhf`Vjo^zu{aw|df}#QngIkm)mM$!Zt%?4BT#$=2%G z;Z6PZ?=$HU^!U@UkK?VD6>+ z2<{#%H2s8nnfqOF?>8_5w$A4;piD)d$ zy){x3FQU-OYrYxxtFNJEdC1K&j6P-FjgfU~G+RrKLfbqVAt67n#je}!`j}k@?fSG` zZ$slXdkT08cnWw5cnWw5cnWw5cnWw5cnWw5cnWw5{09`M-`{YWY1DT~UWFT>DH)1P z$;ny692akyb<176WK);q5{DDxxYS(dC-FOg9dbG%!}HKK0 z(!@iMDslCBJKS;9sgI90aMpRov#9+z*l>O)z;hHp9(5>d!LuB9yKuh?zvII1yYPey z$6c8D;Ucm48wcLU@_w`y1=hv$o9$u(xJBNohg5o0%%+-?iWX~rRa0?-oT;%V#hDIg JBDSmp@i+LF;Jg3; diff --git a/05-record-like/struct/metro_areas.bin b/05-record-like/struct/metro_areas.bin deleted file mode 100644 index 1e738222c9d003f54adb49c2222f45f05d8ba747..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72 zcmaFF&cG0opIw>HaFmsifq}s*fPq0ULfz*+P&_yzF)uwMF%u}v;Oxi1@MD26SllZy UJF%!F5hTpu>GJF4o}(YV0h6{AqW}N^ diff --git a/05-record-like/struct/metro_read.py b/05-record-like/struct/metro_read.py deleted file mode 100644 index 22f315a..0000000 --- a/05-record-like/struct/metro_read.py +++ /dev/null @@ -1,15 +0,0 @@ -from struct import iter_unpack - -FORMAT = 'i12s2sf' # <1> - -def text(field: bytes) -> str: # <2> - octets = field.split(b'\0', 1)[0] # <3> - return octets.decode('cp437') # <4> - -with open('metro_areas.bin', 'rb') as fp: # <5> - data = fp.read() - -for fields in iter_unpack(FORMAT, data): # <6> - year, name, country, pop = fields - place = text(name) + ', ' + text(country) # <7> - print(f'{year}\t{place}\t{pop:,.0f}') diff --git a/05-record-like/struct/metro_write.c b/05-record-like/struct/metro_write.c deleted file mode 100644 index f179370..0000000 --- a/05-record-like/struct/metro_write.c +++ /dev/null @@ -1,46 +0,0 @@ -#include -#include -#include - -#define LEN 3 - -struct MetroArea { - int year; - char name[12]; - char country[2]; - float population; -}; - -int main(int argc, char* argv[]) { - struct MetroArea metro[LEN]; - int rank; - - metro[0].year = 2018; - strcpy(metro[0].name, "Tokyo"); - metro[0].country[0] = 'J'; - metro[0].country[1] = 'P'; - metro[0].population = 43868229.0; - - metro[1].year = 2015; - strcpy(metro[1].name, "Shanghai"); - metro[1].country[0] = 'C'; - metro[1].country[1] = 'N'; - metro[1].population = 38700000.0; - - metro[2].year = 2015; - strcpy(metro[2].name, "Jakarta"); - metro[2].country[0] = 'I'; - metro[2].country[1] = 'D'; - metro[2].population = 31689592.0; - - FILE* data; - if ( (data = fopen("metro_areas.bin", "wb")) == NULL ) { - printf("Error opening file\n"); - return 1; - } - - fwrite(metro, sizeof(struct MetroArea), LEN, data); - fclose(data); - - return 0; -} diff --git a/07-1class-func/clip.py b/07-1class-func/clip.py index 2ca3f43..79411de 100644 --- a/07-1class-func/clip.py +++ b/07-1class-func/clip.py @@ -15,22 +15,26 @@ 'banana' >>> clip('banana split', 12) 'banana split' + >>> clip('bananasplit', 5) + 'bananasplit' + >>> clip('banana split', 8) + 'banana' """ # tag::CLIP[] def clip(text, max_len=80): - """Return text clipped at the last space before or after max_len - """ - end = None - if len(text) > max_len: - space_before = text.rfind(' ', 0, max_len) - if space_before >= 0: - end = space_before - else: - space_after = text.rfind(' ', max_len) - if space_after >= 0: - end = space_after - if end is None: # no spaces were found - return text.rstrip() + """Return text clipped at the last space before or after max_len""" + text = text.rstrip() + end = len(text) + if end <= max_len: + return text + space_before = text.rfind(' ', 0, max_len) + if space_before >= 0: + end = space_before + else: + space_after = text.find(' ', max_len) + if space_after >= 0: + end = space_after return text[:end].rstrip() # end::CLIP[] + diff --git a/07-1class-func/clip_signature.rst b/07-1class-func/clip_signature.rst index 43a5cb4..9bc2dee 100644 --- a/07-1class-func/clip_signature.rst +++ b/07-1class-func/clip_signature.rst @@ -1,8 +1,8 @@ >>> from clip import clip >>> from inspect import signature >>> sig = signature(clip) ->>> sig # doctest: +ELLIPSIS - +>>> sig + >>> str(sig) '(text, max_len=80)' >>> for name, param in sig.parameters.items(): diff --git a/07-1class-func/tagger.py b/07-1class-func/tagger.py index f132bef..b610676 100644 --- a/07-1class-func/tagger.py +++ b/07-1class-func/tagger.py @@ -28,12 +28,9 @@ def tag(name, *content, class_=None, **attrs): """Generate one or more HTML tags""" if class_ is not None: attrs['class'] = class_ - if attrs: - attr_pairs = (f' {attr}="{value}"' for attr, value - in sorted(attrs.items())) - attr_str = ''.join(attr_pairs) - else: - attr_str = '' + attr_pairs = (f' {attr}="{value}"' for attr, value + in sorted(attrs.items())) + attr_str = ''.join(attr_pairs) if content: elements = (f'<{name}{attr_str}>{c}' for c in content) diff --git a/18-context-mngr/README.rst b/18-with-match/README.rst similarity index 100% rename from 18-context-mngr/README.rst rename to 18-with-match/README.rst diff --git a/18-with-match/lisplus/examples_test.py b/18-with-match/lisplus/examples_test.py new file mode 100644 index 0000000..38510a3 --- /dev/null +++ b/18-with-match/lisplus/examples_test.py @@ -0,0 +1,109 @@ +""" +Doctests for `parse`: + +>>> from lis import parse + +# tag::PARSE_DEMO[] +>>> parse('1.5') # <1> +1.5 +>>> parse('set!') # <2> +'set!' +>>> parse('(gcd 18 44)') # <3> +['gcd', 18, 44] +>>> parse('(- m (* n (// m n)))') # <4> +['-', 'm', ['*', 'n', ['//', 'm', 'n']]] + +# end::PARSE_DEMO[] + +""" + +import math + +from lis import run + + +fact_src = """ +(define (! n) + (if (< n 2) + 1 + (* n (! (- n 1))) + ) +) +(! 42) +""" +def test_factorial(): + got = run(fact_src) + assert got == 1405006117752879898543142606244511569936384000000000 + assert got == math.factorial(42) + + +gcd_src = """ +(define (mod m n) + (- m (* n (// m n)))) +(define (gcd m n) + (if (= n 0) + m + (gcd n (mod m n)))) +(gcd 18 45) +""" +def test_gcd(): + got = run(gcd_src) + assert got == 9 + + +quicksort_src = """ +(define (quicksort lst) + (if (null? lst) + lst + (begin + (define pivot (car lst)) + (define rest (cdr lst)) + (append + (quicksort + (filter (lambda (x) (< x pivot)) rest)) + (list pivot) + (quicksort + (filter (lambda (x) (>= x pivot)) rest))) + ) + ) +) +(quicksort (list 2 1 6 3 4 0 8 9 7 5)) +""" +def test_quicksort(): + got = run(quicksort_src) + assert got == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + +# Example from Structure and Interpretation of Computer Programs +# https://mitpress.mit.edu/sites/default/files/sicp/full-text/sicp/book/node12.html + +newton_src = """ +(define (sqrt x) + (sqrt-iter 1.0 x)) +(define (sqrt-iter guess x) + (if (good-enough? guess x) + guess + (sqrt-iter (improve guess x) x))) +(define (good-enough? guess x) + (< (abs (- (* guess guess) x)) 0.001)) +(define (improve guess x) + (average guess (/ x guess))) +(define (average x y) + (/ (+ x y) 2)) +(sqrt 123454321) +""" +def test_newton(): + got = run(newton_src) + assert math.isclose(got, 11111) + + +closure_src = """ +(define (make-adder increment) + (lambda (x) (+ increment x)) +) +(define inc (make-adder 1)) +(inc 99) +""" +def test_newton(): + got = run(closure_src) + assert got == 100 diff --git a/02-array-seq/lispy/lis.py b/18-with-match/lisplus/lis.py similarity index 95% rename from 02-array-seq/lispy/lis.py rename to 18-with-match/lisplus/lis.py index e37ba23..7ae0a9d 100644 --- a/02-array-seq/lispy/lis.py +++ b/18-with-match/lisplus/lis.py @@ -23,7 +23,7 @@ class Procedure: "A user-defined Scheme procedure." - def __init__(self, parms: list[Symbol], body: Expression, env: Environment): + def __init__(self, parms: list[Symbol], body: list[Expression], env: Environment): self.parms = parms self.body = body self.env = env @@ -31,7 +31,9 @@ def __init__(self, parms: list[Symbol], body: Expression, env: Environment): def __call__(self, *args: Expression) -> Any: local_env = dict(zip(self.parms, args)) env: Environment = ChainMap(local_env, self.env) - return evaluate(self.body, env) + for exp in self.body: + result = evaluate(exp, env) + return result ################ global environment @@ -160,9 +162,9 @@ def evaluate(exp: Expression, env: Environment) -> Any: return evaluate(alternative, env) case ['define', Symbol(var), value_exp]: env[var] = evaluate(value_exp, env) - case ['define', [Symbol(name), *parms], body]: + case ['define', [Symbol(name), *parms], *body]: env[name] = Procedure(parms, body, env) - case ['lambda', [*parms], body]: + case ['lambda', [*parms], *body]: return Procedure(parms, body, env) case [op, *args]: proc = evaluate(op, env) diff --git a/18-with-match/lisplus/lis_test.py b/18-with-match/lisplus/lis_test.py new file mode 100644 index 0000000..3688888 --- /dev/null +++ b/18-with-match/lisplus/lis_test.py @@ -0,0 +1,182 @@ +from typing import Optional + +from pytest import mark, fixture + +from lis import parse, evaluate, Expression, Environment, standard_env + +############################################################# tests for parse + +@mark.parametrize( 'source, expected', [ + ('7', 7), + ('x', 'x'), + ('(sum 1 2 3)', ['sum', 1, 2, 3]), + ('(+ (* 2 100) (* 1 10))', ['+', ['*', 2, 100], ['*', 1, 10]]), + ('99 100', 99), # parse stops at the first complete expression + ('(a)(b)', ['a']), +]) +def test_parse(source: str, expected: Expression) -> None: + got = parse(source) + assert got == expected + + +########################################################## tests for evaluate + +# Norvig's tests are not isolated: they assume the +# same environment from first to last test. +global_env_for_first_test = standard_env() + +@mark.parametrize( 'source, expected', [ + ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), + ("(+ 2 2)", 4), + ("(+ (* 2 100) (* 1 10))", 210), + ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), + ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), + ("(define x 3)", None), + ("x", 3), + ("(+ x x)", 6), + ("((lambda (x) (+ x x)) 5)", 10), + ("(define twice (lambda (x) (* 2 x)))", None), + ("(twice 5)", 10), + ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), + ("((compose list twice) 5)", [10]), + ("(define repeat (lambda (f) (compose f f)))", None), + ("((repeat twice) 5)", 20), + ("((repeat (repeat twice)) 5)", 80), + ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), + ("(fact 3)", 6), + ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), + ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), + ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), + ("""(define combine (lambda (f) + (lambda (x y) + (if (null? x) (quote ()) + (f (list (car x) (car y)) + ((combine f) (cdr x) (cdr y)))))))""", None), + ("(define zip (combine cons))", None), + ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), + ("""(define riff-shuffle (lambda (deck) + (begin + (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) + (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) + (define mid (lambda (seq) (/ (length seq) 2))) + ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), + ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), + ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), + ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), +]) +def test_evaluate(source: str, expected: Optional[Expression]) -> None: + got = evaluate(parse(source), global_env_for_first_test) + assert got == expected + + +@fixture +def std_env() -> Environment: + return standard_env() + +# tests for each of the cases in evaluate + +def test_evaluate_variable() -> None: + env: Environment = dict(x=10) + source = 'x' + expected = 10 + got = evaluate(parse(source), env) + assert got == expected + + +def test_evaluate_literal(std_env: Environment) -> None: + source = '3.3' + expected = 3.3 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_quote(std_env: Environment) -> None: + source = '(quote (1.1 is not 1))' + expected = [1.1, 'is', 'not', 1] + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_if_true(std_env: Environment) -> None: + source = '(if 1 10 no-such-thing)' + expected = 10 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_if_false(std_env: Environment) -> None: + source = '(if 0 no-such-thing 20)' + expected = 20 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_define(std_env: Environment) -> None: + source = '(define answer (* 6 7))' + got = evaluate(parse(source), std_env) + assert got is None + assert std_env['answer'] == 42 + + +def test_lambda(std_env: Environment) -> None: + source = '(lambda (a b) (if (>= a b) a b))' + func = evaluate(parse(source), std_env) + assert func.parms == ['a', 'b'] + assert func.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] + assert func.env is std_env + assert func(1, 2) == 2 + assert func(3, 2) == 3 + + +def test_begin(std_env: Environment) -> None: + source = """ + (begin + (define x (* 2 3)) + (* x 7) + ) + """ + got = evaluate(parse(source), std_env) + assert got == 42 + + +def test_invocation_builtin_car(std_env: Environment) -> None: + source = '(car (quote (11 22 33)))' + got = evaluate(parse(source), std_env) + assert got == 11 + + +def test_invocation_builtin_append(std_env: Environment) -> None: + source = '(append (quote (a b)) (quote (c d)))' + got = evaluate(parse(source), std_env) + assert got == ['a', 'b', 'c', 'd'] + + +def test_invocation_builtin_map(std_env: Environment) -> None: + source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))' + got = evaluate(parse(source), std_env) + assert got == [2, 4, 6] + + +def test_invocation_user_procedure(std_env: Environment) -> None: + source = """ + (begin + (define max (lambda (a b) (if (>= a b) a b))) + (max 22 11) + ) + """ + got = evaluate(parse(source), std_env) + assert got == 22 + + +###################################### for py3.10/lis.py only + +def test_define_function(std_env: Environment) -> None: + source = '(define (max a b) (if (>= a b) a b))' + got = evaluate(parse(source), std_env) + assert got is None + max_fn = std_env['max'] + assert max_fn.parms == ['a', 'b'] + assert max_fn.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] + assert max_fn.env is std_env + assert max_fn(1, 2) == 2 + assert max_fn(3, 2) == 3 diff --git a/02-array-seq/lispy/meta_test.py b/18-with-match/lisplus/meta_test.py similarity index 100% rename from 02-array-seq/lispy/meta_test.py rename to 18-with-match/lisplus/meta_test.py diff --git a/18-context-mngr/mirror.py b/18-with-match/mirror.py similarity index 100% rename from 18-context-mngr/mirror.py rename to 18-with-match/mirror.py diff --git a/18-context-mngr/mirror_gen.py b/18-with-match/mirror_gen.py similarity index 100% rename from 18-context-mngr/mirror_gen.py rename to 18-with-match/mirror_gen.py diff --git a/18-context-mngr/mirror_gen_exc.py b/18-with-match/mirror_gen_exc.py similarity index 100% rename from 18-context-mngr/mirror_gen_exc.py rename to 18-with-match/mirror_gen_exc.py diff --git a/25-class-metaprog/checked/decorator/checkeddeco.py b/25-class-metaprog/checked/decorator/checkeddeco.py index fa6abd3..c7d4be1 100644 --- a/25-class-metaprog/checked/decorator/checkeddeco.py +++ b/25-class-metaprog/checked/decorator/checkeddeco.py @@ -59,7 +59,8 @@ ... AttributeError: 'Movie' has no attribute 'director' -The `_as_dict` instance creates a `dict` from the attributes of a `Movie` object:: +The `_asdict` instance method creates a `dict` from the attributes +of a `Movie` object:: >>> movie._asdict() {'title': 'The Godfather', 'year': 1972, 'box_office': 137.0} diff --git a/25-class-metaprog/checked/initsub/checkedlib.py b/25-class-metaprog/checked/initsub/checkedlib.py index 01bc0a8..9b6bf7f 100644 --- a/25-class-metaprog/checked/initsub/checkedlib.py +++ b/25-class-metaprog/checked/initsub/checkedlib.py @@ -58,7 +58,8 @@ ... AttributeError: 'Movie' object has no attribute 'director' -The `_as_dict` instance creates a `dict` from the attributes of a `Movie` object:: +The `_asdict` instance method creates a `dict` from the attributes +of a `Movie` object:: >>> movie._asdict() {'title': 'The Godfather', 'year': 1972, 'box_office': 137.0} diff --git a/25-class-metaprog/checked/metaclass/checkedlib.py b/25-class-metaprog/checked/metaclass/checkedlib.py index 34484ac..8207dbe 100644 --- a/25-class-metaprog/checked/metaclass/checkedlib.py +++ b/25-class-metaprog/checked/metaclass/checkedlib.py @@ -58,7 +58,8 @@ ... AttributeError: 'Movie' object has no attribute 'director' -The `_as_dict` instance creates a `dict` from the attributes of a `Movie` object:: +The `_asdict` instance method creates a `dict` from the attributes +of a `Movie` object:: >>> movie._asdict() {'title': 'The Godfather', 'year': 1972, 'box_office': 137.0} diff --git a/README.md b/README.md index 8149e80..18eaaa2 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Part / Chapter #|Title|Directory|1st ed. Chapter # 2|An Array of Sequences|[02-array-seq](02-array-seq)|2 3|Dictionaries and Sets|[03-dict-set](03-dict-set)|3 4|Text versus Bytes|[04-text-byte](04-text-byte)|4 -5|Record-like Data Structures|[05-record-like](05-record-like)|🆕 +5|Record-like Data Structures|[05-data-classes](05-data-classes)|🆕 6|Object References, Mutability, and Recycling|[06-obj-ref](06-obj-ref)|8 **III – Functions as Objects**| 7|First-Class Funcions|[07-1class-func](07-1class-func)|5 @@ -42,7 +42,7 @@ Part / Chapter #|Title|Directory|1st ed. Chapter # 16|Operator Overloading: Doing It Right|[16-op-overloading](16-op-overloading)|13 **V – Control Flow**| 17|Iterables, Iterators, and Generators|[17-it-generator](17-it-generator)|14 -18|Context Managers and else Blocks|[18-context-mngr](18-context-mngr)|15 +18|Context Managers and else Blocks|[18-with-match](18-with-match)|15 19|Classic Coroutines|[19-coroutine](19-coroutine)|16 20|Concurrency Models in Python|[20-concurrency](20-concurrency)|🆕 21|Concurrency with Futures|[21-futures](21-futures)|17 From fba87b1cffb6ac9b92efb0d58aea35ebe70b5685 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 8 Jul 2021 00:22:48 -0300 Subject: [PATCH 054/127] Update README.md --- 02-array-seq/lispy/py3.9/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/02-array-seq/lispy/py3.9/README.md b/02-array-seq/lispy/py3.9/README.md index db6a11b..75bc6bb 100644 --- a/02-array-seq/lispy/py3.9/README.md +++ b/02-array-seq/lispy/py3.9/README.md @@ -7,7 +7,7 @@ _Luciano Ramalho_ ## Major changes -* Make the `lambda` form accept more than one expression as the body. This is consistent with _Scheme_ syntax, and provides a useful example for the book. To implement this: +* Make the `lambda` form accept more than one expression as the body. This is consistent with [_Scheme_ syntax](https://web.mit.edu/scheme_v9.2/doc/mit-scheme-ref/Lambda-Expressions.html), and provides a useful example for the book. To implement this: * In `Procedure.__call__`: evaluate `self.body` as a list of expressions, instead of a single expression. Return the value of the last expression. * In `evaluate()`: when processing `lambda`, unpack expression into `(_, parms, *body)`, to accept a list of expressions as the body. * Remove the `global_env` global `dict`. It is only used as a default value for the `env` parameter in `evaluate()`, but it is unsafe to use mutable data structures as parameter default values. To implement this: @@ -16,7 +16,7 @@ _Luciano Ramalho_ * Rewrite the custom test script [lispytest.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispytest.py) as [lis_test.py](https://github.com/fluentpython/example-code-2e/blob/master/02-array-seq/lispy/py3.9/lis_test.py): -a standard [pytest](https://docs.pytest.org) test suite including new test cases, preserving all Norvig's the test cases for +a standard [pytest](https://docs.pytest.org) test suite including new test cases, preserving all Norvig's test cases for [lis.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lis.py) but removing the test cases for the features implemented only in [lispy.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispy.py) From a77e6d1e9797a5d7f7b8e32ed39c094109c8b36e Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 8 Jul 2021 00:23:56 -0300 Subject: [PATCH 055/127] Update README.md --- 02-array-seq/lispy/py3.9/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/02-array-seq/lispy/py3.9/README.md b/02-array-seq/lispy/py3.9/README.md index 75bc6bb..fdb1f08 100644 --- a/02-array-seq/lispy/py3.9/README.md +++ b/02-array-seq/lispy/py3.9/README.md @@ -19,7 +19,7 @@ _Luciano Ramalho_ a standard [pytest](https://docs.pytest.org) test suite including new test cases, preserving all Norvig's test cases for [lis.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lis.py) but removing the test cases for the features implemented only in -[lispy.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispy.py) +[lispy.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispy.py). ## Minor changes From dfef30981edc398c6b7cc1ecc5f71612ab0832e7 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 8 Jul 2021 12:41:18 -0300 Subject: [PATCH 056/127] Update README.md --- 02-array-seq/lispy/README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/02-array-seq/lispy/README.md b/02-array-seq/lispy/README.md index 38458bb..9ac6f56 100644 --- a/02-array-seq/lispy/README.md +++ b/02-array-seq/lispy/README.md @@ -23,14 +23,4 @@ The copyright holder is Peter Norvig and the code is licensed under the [MIT license](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/LICENSE). -## Changes to Norvig's code - -I made small changes to the programs in `original/`: - -* In `lis.py`: - * The `Procedure` class accepts a list of expressions as the `body`, and `__call__` evaluates those expressions in order, and returns the value of the last. This is consistent with Scheme's `lambda` syntax and provided a useful example for pattern matching. - * In the `elif` block for `'lambda'`, I added the `*` in front of the `*body` variable in the tuple unpacking to capture the expressions as a list, before calling the `Procedure` constructor. - -* In `lispy.py` I made [changes and a pull request](https://github.com/norvig/pytudes/pull/106) to make it run on Python 3. - _Luciano Ramalho
June 29, 2021_ From 1283115921091cbe11e22d44279556da59b96e2f Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 10 Jul 2021 11:58:24 -0300 Subject: [PATCH 057/127] sync with Atlas --- 02-array-seq/match_lat_lon.py | 4 +- 04-text-byte/charfinder/README.rst | 72 +++++++++++++++++++++++++++ 04-text-byte/charfinder/cf.py | 6 --- 04-text-byte/charfinder/cf_tests.rst | 36 -------------- 04-text-byte/charfinder/test.sh | 2 +- 07-1class-func/clip.py | 48 ++++++++++-------- 07-1class-func/clip_introspection.rst | 2 +- 15-more-types/typeddict/books.py | 8 +-- 8 files changed, 108 insertions(+), 70 deletions(-) create mode 100644 04-text-byte/charfinder/README.rst delete mode 100644 04-text-byte/charfinder/cf_tests.rst diff --git a/02-array-seq/match_lat_lon.py b/02-array-seq/match_lat_lon.py index 20c7131..d5a5463 100644 --- a/02-array-seq/match_lat_lon.py +++ b/02-array-seq/match_lat_lon.py @@ -23,8 +23,8 @@ def main(): print(f'{"":15} | {"latitude":>9} | {"longitude":>9}') for record in metro_areas: - match record: - case [name, _, _, (lat, lon)] if lon <= 0: + match record: # <1> + case [name, _, _, (lat, lon)] if lon <= 0: # <2> print(f'{name:15} | {lat:9.4f} | {lon:9.4f}') # end::MAIN[] diff --git a/04-text-byte/charfinder/README.rst b/04-text-byte/charfinder/README.rst new file mode 100644 index 0000000..46a5d70 --- /dev/null +++ b/04-text-byte/charfinder/README.rst @@ -0,0 +1,72 @@ +======================== +Character Finder Utility +======================== + +Usage tips +========== + +`cf.py` works as an executable on Unix-like systems, +if you have `python3` on your `$PATH`:: + + $ chmod +x cf.py + $ ./cf.py cat eyes + U+1F638 😸 GRINNING CAT FACE WITH SMILING EYES + U+1F63B 😻 SMILING CAT FACE WITH HEART-SHAPED EYES + U+1F63D 😽 KISSING CAT FACE WITH CLOSED EYES + +Use `wc -l` to count the number of hits:: + + $ ./cf.py hieroglyph | wc -l + 1663 + +With `tee` you can get the output and the count:: + + $ ./cf.py trigram | tee >(wc -l) + U+2630 ☰ TRIGRAM FOR HEAVEN + U+2631 ☱ TRIGRAM FOR LAKE + U+2632 ☲ TRIGRAM FOR FIRE + U+2633 ☳ TRIGRAM FOR THUNDER + U+2634 ☴ TRIGRAM FOR WIND + U+2635 ☵ TRIGRAM FOR WATER + U+2636 ☶ TRIGRAM FOR MOUNTAIN + U+2637 ☷ TRIGRAM FOR EARTH + 8 + + +Running the tests +================= + +Run the ``doctest`` module from the command line on +this README.rst file (using ``-v`` to make tests visible):: + + $ python3 -m doctest README.rst -v + +That's what the ``test.sh`` script does. + + +Tests +----- + +Import functions for testing:: + + >>> from cf import find, main + +Test ``find`` with single result:: + + >>> find('sign', 'registered') # doctest:+NORMALIZE_WHITESPACE + U+00AE ® REGISTERED SIGN + +Test ``find`` with two results:: + + >>> find('chess', 'queen', last=0xFFFF) # doctest:+NORMALIZE_WHITESPACE + U+2655 ♕ WHITE CHESS QUEEN + U+265B ♛ BLACK CHESS QUEEN + +Test ``find`` with no results:: + + >>> find('no_such_character') + +Test ``main`` with no words:: + + >>> main([]) + Please provide words to find. diff --git a/04-text-byte/charfinder/cf.py b/04-text-byte/charfinder/cf.py index 8976a66..28f20d0 100755 --- a/04-text-byte/charfinder/cf.py +++ b/04-text-byte/charfinder/cf.py @@ -4,18 +4,13 @@ FIRST, LAST = ord(' '), sys.maxunicode # <1> - def find(*query_words, first=FIRST, last=LAST): # <2> query = {w.upper() for w in query_words} # <3> - count = 0 for code in range(first, last + 1): char = chr(code) # <4> name = unicodedata.name(char, None) # <5> if name and query.issubset(name.split()): # <6> print(f'U+{code:04X}\t{char}\t{name}') # <7> - count += 1 - print(f'({count} found)') - def main(words): if words: @@ -23,6 +18,5 @@ def main(words): else: print('Please provide words to find.') - if __name__ == '__main__': main(sys.argv[1:]) diff --git a/04-text-byte/charfinder/cf_tests.rst b/04-text-byte/charfinder/cf_tests.rst deleted file mode 100644 index 57028e0..0000000 --- a/04-text-byte/charfinder/cf_tests.rst +++ /dev/null @@ -1,36 +0,0 @@ -Doctests for ``cf.py`` -====================== - -How to run the tests ----------------------- - -Run the ``doctest`` module from the command line:: - - $ python3 -m doctest cf_tests.rst - - -Tests ------ - -Import functions for testing:: - - >>> from cf import find, main - -Test ``find`` with single result:: - - >>> find("sign", "registered") # doctest:+NORMALIZE_WHITESPACE - U+00AE ® REGISTERED SIGN - (1 found) - - -Test ``find`` with two results:: - - >>> find("chess", "queen", last=0xFFFF) # doctest:+NORMALIZE_WHITESPACE - U+2655 ♕ WHITE CHESS QUEEN - U+265B ♛ BLACK CHESS QUEEN - (2 found) - -Test ``main`` with no words:: - - >>> main([]) - Please provide words to find. diff --git a/04-text-byte/charfinder/test.sh b/04-text-byte/charfinder/test.sh index 055fa84..23d19cd 100755 --- a/04-text-byte/charfinder/test.sh +++ b/04-text-byte/charfinder/test.sh @@ -1,2 +1,2 @@ #!/bin/bash -python3 -m doctest cf_tests.rst $1 +python3 -m doctest README.rst $1 diff --git a/07-1class-func/clip.py b/07-1class-func/clip.py index 79411de..2f97c66 100644 --- a/07-1class-func/clip.py +++ b/07-1class-func/clip.py @@ -1,40 +1,48 @@ """ - >>> clip('banana ', 6) - 'banana' - >>> clip('banana ', 7) - 'banana' - >>> clip('banana ', 5) + >>> clip('banana split', 5) 'banana' >>> clip('banana split', 6) 'banana' >>> clip('banana split', 7) 'banana' - >>> clip('banana split', 10) + >>> clip('banana split', 8) 'banana' >>> clip('banana split', 11) 'banana' >>> clip('banana split', 12) 'banana split' - >>> clip('bananasplit', 5) - 'bananasplit' - >>> clip('banana split', 8) - 'banana' + >>> clip('banana-split', 3) + 'banana-split' + +Jess' tests: + + >>> text = 'The quick brown fox jumps over the lazy dog.' + >>> clip14 = clip(text, max_len=14) + >>> clip14 + 'The quick' + >>> len(clip14) + 9 + >>> clip15 = clip(text, max_len=15) + >>> clip15 + 'The quick brown' + >>> len(clip15) + 15 + """ # tag::CLIP[] def clip(text, max_len=80): - """Return text clipped at the last space before or after max_len""" + """Return max_len characters clipped at space if possible""" text = text.rstrip() - end = len(text) - if end <= max_len: + if len(text) <= max_len or ' ' not in text: return text - space_before = text.rfind(' ', 0, max_len) - if space_before >= 0: - end = space_before + end = len(text) + space_at = text.rfind(' ', 0, max_len + 1) + if space_at >= 0: + end = space_at else: - space_after = text.find(' ', max_len) - if space_after >= 0: - end = space_after + space_at = text.find(' ', max_len) + if space_at >= 0: + end = space_at return text[:end].rstrip() # end::CLIP[] - diff --git a/07-1class-func/clip_introspection.rst b/07-1class-func/clip_introspection.rst index aae210a..0b4334d 100644 --- a/07-1class-func/clip_introspection.rst +++ b/07-1class-func/clip_introspection.rst @@ -4,6 +4,6 @@ >>> clip.__code__ # doctest: +ELLIPSIS >>> clip.__code__.co_varnames -('text', 'max_len', 'end', 'space_before', 'space_after') +('text', 'max_len', 'end', 'space_at') >>> clip.__code__.co_argcount 2 diff --git a/15-more-types/typeddict/books.py b/15-more-types/typeddict/books.py index 5a0bc82..e33e21e 100644 --- a/15-more-types/typeddict/books.py +++ b/15-more-types/typeddict/books.py @@ -1,11 +1,11 @@ # tag::BOOKDICT[] -from typing import TypedDict, List +from typing import TypedDict import json class BookDict(TypedDict): isbn: str title: str - authors: List[str] + authors: list[str] pagecount: int # end::BOOKDICT[] @@ -13,7 +13,7 @@ class BookDict(TypedDict): AUTHOR_EL = '{}' def to_xml(book: BookDict) -> str: # <1> - elements: List[str] = [] # <2> + elements: list[str] = [] # <2> for key, value in book.items(): if isinstance(value, list): # <3> elements.extend( @@ -29,4 +29,4 @@ def to_xml(book: BookDict) -> str: # <1> def from_json(data: str) -> BookDict: whatever: BookDict = json.loads(data) # <1> return whatever # <2> -# end::FROMJSON[] \ No newline at end of file +# end::FROMJSON[] From 3f7c926067cd1c66429639b3a12f637556b314a9 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 10 Jul 2021 12:03:04 -0300 Subject: [PATCH 058/127] Update README.md --- 02-array-seq/lispy/py3.9-no-hints/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/02-array-seq/lispy/py3.9-no-hints/README.md b/02-array-seq/lispy/py3.9-no-hints/README.md index db6a11b..2b6f4ea 100644 --- a/02-array-seq/lispy/py3.9-no-hints/README.md +++ b/02-array-seq/lispy/py3.9-no-hints/README.md @@ -5,7 +5,7 @@ use in _Fluent Python, Second Edition_, I made a few changes for didactic reason _Luciano Ramalho_ -## Major changes +## Significant changes * Make the `lambda` form accept more than one expression as the body. This is consistent with _Scheme_ syntax, and provides a useful example for the book. To implement this: * In `Procedure.__call__`: evaluate `self.body` as a list of expressions, instead of a single expression. Return the value of the last expression. @@ -22,7 +22,7 @@ but removing the test cases for the features implemented only in [lispy.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispy.py) -## Minor changes +## Name changes Cosmetic changes to make the code look more familiar to Python programmers, the audience of _Fluent Python_. From 837b5a36bf2a0b4d16e01502f4ed53ca95d34a6a Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 10 Jul 2021 12:15:02 -0300 Subject: [PATCH 059/127] New titles for chapters 4, 5, and 7 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 18eaaa2..102993a 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,11 @@ Part / Chapter #|Title|Directory|1st ed. Chapter # **II – Data Structures**| 2|An Array of Sequences|[02-array-seq](02-array-seq)|2 3|Dictionaries and Sets|[03-dict-set](03-dict-set)|3 -4|Text versus Bytes|[04-text-byte](04-text-byte)|4 -5|Record-like Data Structures|[05-data-classes](05-data-classes)|🆕 +4|Unicode Text versus Bytes|[04-text-byte](04-text-byte)|4 +5|Data Class Builders|[05-data-classes](05-data-classes)|🆕 6|Object References, Mutability, and Recycling|[06-obj-ref](06-obj-ref)|8 **III – Functions as Objects**| -7|First-Class Funcions|[07-1class-func](07-1class-func)|5 +7|Funcions as First-Class Objects|[07-1class-func](07-1class-func)|5 8|Type Hints in Function Definitions|[08-def-type-hints](08-def-type-hints)|🆕 9|Function Decorators and Closures|[09-closure-deco](09-closure-deco)|7 10|Design Patterns with First-Class Functions|[10-dp-1class-func](10-dp-1class-func)|6 From fa1c0a9fe393bb05b21fd71c44b8567796592b22 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Fri, 30 Jul 2021 14:15:38 -0300 Subject: [PATCH 060/127] updated example, thanks to @leorochael --- 14-inheritance/strkeydict_dictsub.py | 93 ++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 14-inheritance/strkeydict_dictsub.py diff --git a/14-inheritance/strkeydict_dictsub.py b/14-inheritance/strkeydict_dictsub.py new file mode 100644 index 0000000..68d7f00 --- /dev/null +++ b/14-inheritance/strkeydict_dictsub.py @@ -0,0 +1,93 @@ +"""StrKeyDict always converts non-string keys to `str` + +This is a variation of `strkeydict.StrKeyDict` implemented +as a `dict` built-in subclass (instead of a `UserDict` subclass) + +Test for initializer: keys are converted to `str`. + + >>> d = StrKeyDict([(2, 'two'), ('4', 'four')]) + >>> sorted(d.keys()) + ['2', '4'] + +Tests for item retrieval using `d[key]` notation:: + + >>> d['2'] + 'two' + >>> d[4] + 'four' + >>> d[1] + Traceback (most recent call last): + ... + KeyError: '1' + +Tests for item retrieval using `d.get(key)` notation:: + + >>> d.get('2') + 'two' + >>> d.get(4) + 'four' + >>> d.get(1, 'N/A') + 'N/A' + +Tests for the `in` operator:: + + >>> 2 in d + True + >>> 1 in d + False + +Test for item assignment using non-string key:: + + >>> d[0] = 'zero' + >>> d['0'] + 'zero' + +Tests for update using a `dict` or a sequence of pairs:: + + >>> d.update({6:'six', '8':'eight'}) + >>> sorted(d.keys()) + ['0', '2', '4', '6', '8'] + >>> d.update([(10, 'ten'), ('12', 'twelve')]) + >>> sorted(d.keys()) + ['0', '10', '12', '2', '4', '6', '8'] + >>> d.update([1, 3, 5]) + Traceback (most recent call last): + ... + TypeError: cannot unpack non-iterable int object + +""" + + +class StrKeyDict(dict): + + def __init__(self, iterable=None, **kwds): + super().__init__() + self.update(iterable, **kwds) + + def __missing__(self, key): + if isinstance(key, str): + raise KeyError(key) + return self[str(key)] + + def __contains__(self, key): + return key in self.keys() or str(key) in self.keys() + + def __setitem__(self, key, item): + super().__setitem__(str(key), item) + + def get(self, key, default=None): + try: + return self[key] + except KeyError: + return default + + def update(self, iterable=None, **kwds): + if iterable is not None: + try: # duck typing FTW! + pairs = iterable.items() + except AttributeError: + pairs = iterable + for key, value in pairs: + self[key] = value + if kwds: + self.update(kwds) From b4ad845d12faf5923370cc47e9e5889acf2f3a72 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 2 Aug 2021 22:00:17 -0300 Subject: [PATCH 061/127] missing.py example --- 03-dict-set/missing.py | 122 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 03-dict-set/missing.py diff --git a/03-dict-set/missing.py b/03-dict-set/missing.py new file mode 100644 index 0000000..609d39e --- /dev/null +++ b/03-dict-set/missing.py @@ -0,0 +1,122 @@ +""" +Semantics of ``__missing__`` across mappings. + +✅ = indicates ``__missing__`` was called + +Subclass of ``dict``:: + + >>> d = DictSub(A = 'letter A') + >>> d['a'] # ✅ + 'letter A' + >>> d.get('a', '') + '' + >>> 'a' in d + False + +Subclass of ``UserDict``:: + + >>> ud = UserDictSub(A = 'letter A') + >>> ud['a'] # ✅ + 'letter A' + >>> ud.get('a', '') # ✅ + 'letter A' + >>> 'a' in ud + False + + +Simple subclass of ``abc.Mapping``:: + + >>> sms = SimpleMappingSub(A = 'letter A') + >>> sms['a'] + Traceback (most recent call last): + ... + KeyError: 'a' + >>> sms.get('a', '') + '' + >>> 'a' in sms + False + + +Subclass of ``abc.Mapping`` with support for ``__missing__``:: + + >>> mms = MappingMissingSub(A = 'letter A') + >>> mms['a'] # ✅ + 'letter A' + >>> mms.get('a', '') # ✅ + 'letter A' + >>> 'a' in mms # ✅ + True + +Subclass of ``abc.Mapping`` with support for ``__missing__``:: + + >>> dms = DictLikeMappingSub(A = 'letter A') + >>> dms['a'] # ✅ + 'letter A' + >>> dms.get('a', '') + '' + >>> 'a' in dms + False + + +""" + +from collections import UserDict +from collections import abc + + +def _upper(x): + try: + return x.upper() + except AttributeError: + return x + + +class DictSub(dict): + def __missing__(self, key): + return self[_upper(key)] + + +class UserDictSub(UserDict): + def __missing__(self, key): + return self[_upper(key)] + + +class SimpleMappingSub(abc.Mapping): + def __init__(self, *args, **kwargs): + self._data = dict(*args, **kwargs) + + # next three methods: abstract in ABC + def __getitem__(self, key): + return self._data[key] + + def __len__(self): + return len(self._data) + + def __iter__(self): + return iter(self._data) + + # never called by instances of this class + def __missing__(self, key): + return self[_upper(key)] + + +class MappingMissingSub(SimpleMappingSub): + def __getitem__(self, key): + try: + return self._data[key] + except KeyError: + return self[_upper(key)] + + +class DictLikeMappingSub(SimpleMappingSub): + def __getitem__(self, key): + try: + return self._data[key] + except KeyError: + return self[_upper(key)] + + def get(self, key, default=None): + return self._data.get(key, default) + + def __contains__(self, key): + return key in self._data From cbd13885fc1a7903be433277791ff9d1868be752 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Fri, 6 Aug 2021 19:51:22 -0300 Subject: [PATCH 062/127] updates to ch.14 --- 14-inheritance/README.rst | 2 +- 14-inheritance/diamond.py | 65 +++++++++++++++----- 14-inheritance/diamond2.py | 67 ++++++++++++++++++++ 14-inheritance/uppermixin.py | 115 +++++++++++++++++++++++++++++++++++ 4 files changed, 233 insertions(+), 16 deletions(-) create mode 100644 14-inheritance/diamond2.py create mode 100644 14-inheritance/uppermixin.py diff --git a/14-inheritance/README.rst b/14-inheritance/README.rst index 2f7426c..496ffb2 100644 --- a/14-inheritance/README.rst +++ b/14-inheritance/README.rst @@ -1,4 +1,4 @@ -Sample code for Chapter 14 - "Inheritance: for good or for worse" +Sample code for Chapter 14 - "Inheritance: for better or for worse" From the book "Fluent Python, Second Edition" by Luciano Ramalho (O'Reilly, 2021) https://learning.oreilly.com/library/view/fluent-python-2nd/9781492056348/ diff --git a/14-inheritance/diamond.py b/14-inheritance/diamond.py index 5aaac18..1df747b 100644 --- a/14-inheritance/diamond.py +++ b/14-inheritance/diamond.py @@ -1,27 +1,62 @@ -class A: - def ping(self): - print('ping:', self) +""" +diamond1.py: Demo of diamond-shaped class graph. +# tag::LEAF_MRO[] +>>> Leaf.__mro__ # doctest:+NORMALIZE_WHITESPACE + (, , , + , ) -class B(A): - def pong(self): - print('pong:', self) +# end::LEAF_MRO[] + +# tag::DIAMOND_CALLS[] + >>> leaf1 = Leaf() # <1> + >>> leaf1.ping() # <2> + .ping() in Leaf + .ping() in A + .ping() in B + .ping() in Root + >>> leaf1.pong() # <3> + .pong() in A + .pong() in B + +# end::DIAMOND_CALLS[] +""" + +# tag::DIAMOND_CLASSES[] +class Root: # <1> + def ping(self): + print(f'{self}.ping() in Root') -class C(A): def pong(self): - print('PONG:', self) + print(f'{self}.pong() in Root') + def __repr__(self): + cls_name = type(self).__name__ + return f'' -class D(B, C): +class A(Root): # <2> def ping(self): + print(f'{self}.ping() in A') super().ping() - print('post-ping:', self) - def pingpong(self): - self.ping() - super().ping() - self.pong() + def pong(self): + print(f'{self}.pong() in A') super().pong() - C.pong(self) + + +class B(Root): # <3> + def ping(self): + print(f'{self}.ping() in B') + super().ping() + + def pong(self): + print(f'{self}.pong() in B') + + +class Leaf(A, B): # <4> + def ping(self): + print(f'{self}.ping() in Leaf') + super().ping() +# end::DIAMOND_CLASSES[] diff --git a/14-inheritance/diamond2.py b/14-inheritance/diamond2.py new file mode 100644 index 0000000..d92db69 --- /dev/null +++ b/14-inheritance/diamond2.py @@ -0,0 +1,67 @@ +""" +unrelated.py: examples with ``super()`` in a sibling class. + +``U`` is unrelated (does not subclass ``Root``) + +Calling ``ping`` on an instance of ``U`` fails:: + +# tag::UNRELATED_DEMO_1[] + >>> u = U() + >>> u.ping() + Traceback (most recent call last): + ... + AttributeError: 'super' object has no attribute 'ping' + +# end::UNRELATED_DEMO_1[] + + +But if ``U`` is part of a cooperative arrangement of base classes, +its ``ping`` method works:: + +# tag::UNRELATED_DEMO_2[] + + >>> leaf2 = LeafUA() + >>> leaf2.ping() + .ping() in LeafUA + .ping() in U + .ping() in A + .ping() in Root + >>> LeafUA.__mro__ # doctest:+NORMALIZE_WHITESPACE + (, , + , , ) + +# end::UNRELATED_DEMO_2[] + + +Here ``U.ping`` is never called because ``Root.ping`` does not call ``super``. + + >>> o6 = LeafAU() + >>> o6.ping() + .ping() in LeafAU + .ping() in A + .ping() in Root + >>> LeafAU.__mro__ # doctest:+NORMALIZE_WHITESPACE + (, , , + , ) + +""" + +# tag::DIAMOND_CLASSES[] +from diamond import A # <1> + +class U(): # <2> + def ping(self): + print(f'{self}.ping() in U') + super().ping() # <3> + +class LeafUA(U, A): # <4> + def ping(self): + print(f'{self}.ping() in LeafUA') + super().ping() +# end::DIAMOND_CLASSES[] + +class LeafAU(A, U): + def ping(self): + print(f'{self}.ping() in LeafAU') + super().ping() + diff --git a/14-inheritance/uppermixin.py b/14-inheritance/uppermixin.py new file mode 100644 index 0000000..d86ecea --- /dev/null +++ b/14-inheritance/uppermixin.py @@ -0,0 +1,115 @@ +"""UpperDict uppercases all string keys. + +Test for initializer. `str` keys are uppercased:: + + >>> d = UpperDict([('a', 'letter A'), ('B', 'letter B'), (2, 'digit two')]) + >>> list(d.keys()) + ['A', 'B', 2] + +Tests for item retrieval using `d[key]` notation:: + + >>> d['A'] + 'letter A' + >>> d['b'] + 'letter B' + >>> d[2] + 'digit two' + + +Tests for missing key:: + + >>> d['z'] + Traceback (most recent call last): + ... + KeyError: 'Z' + >>> d[99] + Traceback (most recent call last): + ... + KeyError: 99 + + +Tests for item retrieval using `d.get(key)` notation:: + + >>> d.get('a') + 'letter A' + >>> d.get('B') + 'letter B' + >>> d.get(2) + 'digit two' + >>> d.get('z', '(not found)') + '(not found)' + +Tests for the `in` operator:: + + >>> ('a' in d, 'B' in d, 'z' in d) + (True, True, False) + +Test for item assignment using lowercase key:: + + >>> d['c'] = 'letter C' + >>> d['C'] + 'letter C' + +Tests for update using a `dict` or a sequence of pairs:: + + >>> d.update({'D': 'letter D', 'e': 'letter E'}) + >>> list(d.keys()) + ['A', 'B', 2, 'C', 'D', 'E'] + >>> d.update([('f', 'letter F'), ('G', 'letter G')]) + >>> list(d.keys()) + ['A', 'B', 2, 'C', 'D', 'E', 'F', 'G'] + >>> d # doctest:+NORMALIZE_WHITESPACE + {'A': 'letter A', 'B': 'letter B', 2: 'digit two', + 'C': 'letter C', 'D': 'letter D', 'E': 'letter E', + 'F': 'letter F', 'G': 'letter G'} + +UpperCounter uppercases all `str` keys. + +Test for initializer: keys are uppercased. + + >>> d = UpperCounter('AbracAdaBrA') + >>> sorted(d.keys()) + ['A', 'B', 'C', 'D', 'R'] + +Tests for count retrieval using `d[key]` notation:: + + >>> d['a'] + 5 + >>> d['z'] + 0 + +""" +# tag::UPPERCASE_MIXIN[] +import collections + + +def _upper(key): # <1> + try: + return key.upper() + except AttributeError: + return key + + +class UpperCaseMixin: # <2> + def __setitem__(self, key, item): + super().__setitem__(_upper(key), item) + + def __getitem__(self, key): + return super().__getitem__(_upper(key)) + + def get(self, key, default=None): + return super().get(_upper(key), default) + + def __contains__(self, key): + return super().__contains__(_upper(key)) +# end::UPPERCASE_MIXIN[] + +# tag::UPPERDICT[] +class UpperDict(UpperCaseMixin, collections.UserDict): # <1> + pass + + +class UpperCounter(UpperCaseMixin, collections.Counter): # <2> + """Specialized 'Counter' that uppercases string keys""" # <3> + +# end::UPPERDICT[] From 01e717b60a9a14bc1bca4bffd915f7ed2c637e5e Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 7 Aug 2021 00:44:01 -0300 Subject: [PATCH 063/127] updated from Atlas --- 02-array-seq/lispy/README.md | 10 ++ 04-text-byte/charfinder/README.rst | 2 +- 04-text-byte/charfinder/cf.py | 6 +- 05-data-classes/dataclass/club.py | 2 - 05-data-classes/dataclass/club_wrong.py | 1 - 05-data-classes/dataclass/hackerclub.py | 2 - .../dataclass/hackerclub_annotated.py | 2 - 05-data-classes/dataclass/resource.py | 2 +- 05-data-classes/meaning/demo_dc.py | 1 - 05-data-classes/meaning/demo_nt.py | 1 - 05-data-classes/meaning/demo_plain.py | 1 - .../typing_namedtuple/coordinates.py | 1 - .../typing_namedtuple/coordinates2.py | 3 +- .../typing_namedtuple/nocheck_demo.py | 1 - 07-1class-func/clip.py | 48 --------- 07-1class-func/clip_introspection.rst | 9 -- 07-1class-func/clip_signature.rst | 12 --- 08-def-type-hints/RPN_calc/calc.py | 67 ------------ 08-def-type-hints/RPN_calc/calc_test.py | 51 --------- 08-def-type-hints/charindex.py | 6 +- 08-def-type-hints/colors.py | 6 +- 08-def-type-hints/columnize.py | 4 +- 08-def-type-hints/columnize2.py | 35 ------ 08-def-type-hints/columnize_alias.py | 26 ----- 08-def-type-hints/comparable/top.py | 9 +- 08-def-type-hints/comparable/top_test.py | 25 +++-- 08-def-type-hints/coordinates/coordinates.py | 4 +- .../coordinates/coordinates_named.py | 13 ++- 08-def-type-hints/list.py | 10 -- .../messages/hints_1/messages.py | 11 +- .../messages/hints_1/messages_test.py | 4 +- .../messages/hints_2/messages.py | 19 ++-- .../messages/hints_2/messages_test.py | 10 +- .../messages/no_hints/messages.py | 11 +- .../messages/no_hints/messages_test.py | 2 +- 08-def-type-hints/mode/mode_T.py | 26 ----- 08-def-type-hints/mode/mode_float.py | 4 +- 08-def-type-hints/mode/mode_hashable.py | 3 +- 08-def-type-hints/mode/mode_hashable_wrong.py | 24 ----- 08-def-type-hints/mode/mode_number.py | 26 ----- .../{messages/hints_1 => }/mypy.ini | 4 +- 08-def-type-hints/passdrill.py | 101 ------------------ 08-def-type-hints/replacer.py | 4 +- 08-def-type-hints/replacer2.py | 39 ------- 08-def-type-hints/reveal_array.py | 8 -- 08-def-type-hints/sample.py | 5 +- 08-def-type-hints/typevar_bounded.py | 11 ++ 08-def-type-hints/typevars_constrained.py | 4 +- 09-closure-deco/{ => clock}/clockdeco.py | 0 09-closure-deco/{ => clock}/clockdeco0.py | 0 09-closure-deco/{ => clock}/clockdeco_cls.py | 0 09-closure-deco/{ => clock}/clockdeco_demo.py | 5 +- .../{ => clock}/clockdeco_param.py | 0 .../{ => clock}/clockdeco_param_demo1.py | 0 .../{ => clock}/clockdeco_param_demo2.py | 0 10-dp-1class-func/classic_strategy.py | 82 +++++++------- 10-dp-1class-func/classic_strategy_test.py | 44 ++++---- .../monkeytype/classic_strategy.py | 4 +- 10-dp-1class-func/promotions.py | 21 ++-- 10-dp-1class-func/pytypes/classic_strategy.py | 4 +- 10-dp-1class-func/requirements.txt | 15 +-- 10-dp-1class-func/strategy.py | 82 +++++++------- 10-dp-1class-func/strategy_best.py | 24 +++-- 10-dp-1class-func/strategy_best2.py | 99 ++++------------- 10-dp-1class-func/strategy_best3.py | 67 +++--------- 10-dp-1class-func/strategy_best4.py | 101 ++++++------------ 10-dp-1class-func/strategy_param.py | 4 +- 10-dp-1class-func/strategy_param_test.py | 2 +- 10-dp-1class-func/strategy_test.py | 45 ++++---- 10-dp-1class-func/untyped/classic_strategy.py | 4 +- 10-dp-1class-func/untyped/strategy.py | 4 +- 10-dp-1class-func/untyped/strategy_best.py | 6 +- 10-dp-1class-func/untyped/strategy_best2.py | 6 +- 10-dp-1class-func/untyped/strategy_best3.py | 6 +- 10-dp-1class-func/untyped/strategy_best4.py | 6 +- 10-dp-1class-func/untyped/strategy_param.py | 4 +- 10-dp-1class-func/untyped/strategy_param2.py | 4 +- 11-pythonic-obj/mem_test.py | 15 ++- 11-pythonic-obj/patterns.py | 53 +++++++++ 11-pythonic-obj/slots.rst | 52 +++++++++ 11-pythonic-obj/vector2d_v3.py | 4 +- 11-pythonic-obj/vector2d_v3_prophash.py | 4 +- 11-pythonic-obj/vector2d_v3_slots.py | 7 +- 12-seq-hacking/vector_v3.py | 20 ++-- 12-seq-hacking/vector_v4.py | 12 ++- 12-seq-hacking/vector_v5.py | 12 ++- 13-protocol-abc/frenchdeck2.py | 6 +- 13-protocol-abc/typing/randompick_test.py | 8 +- 13-protocol-abc/typing/vector2d_v4.py | 2 +- 13-protocol-abc/typing/vector2d_v5.py | 2 +- 14-inheritance/uppermixin.py | 41 +++++-- 15-more-types/protocol/abs_demo.py | 2 + 16-op-overloading/vector2d_v3.py | 8 +- 16-op-overloading/vector_v6.py | 12 ++- 16-op-overloading/vector_v7.py | 12 ++- 16-op-overloading/vector_v8.py | 12 ++- 96 files changed, 579 insertions(+), 1020 deletions(-) delete mode 100644 07-1class-func/clip.py delete mode 100644 07-1class-func/clip_introspection.rst delete mode 100644 07-1class-func/clip_signature.rst delete mode 100755 08-def-type-hints/RPN_calc/calc.py delete mode 100644 08-def-type-hints/RPN_calc/calc_test.py delete mode 100644 08-def-type-hints/columnize2.py delete mode 100644 08-def-type-hints/columnize_alias.py delete mode 100644 08-def-type-hints/list.py delete mode 100644 08-def-type-hints/mode/mode_T.py delete mode 100644 08-def-type-hints/mode/mode_hashable_wrong.py delete mode 100644 08-def-type-hints/mode/mode_number.py rename 08-def-type-hints/{messages/hints_1 => }/mypy.ini (50%) delete mode 100755 08-def-type-hints/passdrill.py delete mode 100644 08-def-type-hints/replacer2.py delete mode 100644 08-def-type-hints/reveal_array.py create mode 100644 08-def-type-hints/typevar_bounded.py rename 09-closure-deco/{ => clock}/clockdeco.py (100%) rename 09-closure-deco/{ => clock}/clockdeco0.py (100%) rename 09-closure-deco/{ => clock}/clockdeco_cls.py (100%) rename 09-closure-deco/{ => clock}/clockdeco_demo.py (90%) rename 09-closure-deco/{ => clock}/clockdeco_param.py (100%) rename 09-closure-deco/{ => clock}/clockdeco_param_demo1.py (100%) rename 09-closure-deco/{ => clock}/clockdeco_param_demo2.py (100%) create mode 100644 11-pythonic-obj/patterns.py create mode 100644 11-pythonic-obj/slots.rst diff --git a/02-array-seq/lispy/README.md b/02-array-seq/lispy/README.md index 9ac6f56..38458bb 100644 --- a/02-array-seq/lispy/README.md +++ b/02-array-seq/lispy/README.md @@ -23,4 +23,14 @@ The copyright holder is Peter Norvig and the code is licensed under the [MIT license](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/LICENSE). +## Changes to Norvig's code + +I made small changes to the programs in `original/`: + +* In `lis.py`: + * The `Procedure` class accepts a list of expressions as the `body`, and `__call__` evaluates those expressions in order, and returns the value of the last. This is consistent with Scheme's `lambda` syntax and provided a useful example for pattern matching. + * In the `elif` block for `'lambda'`, I added the `*` in front of the `*body` variable in the tuple unpacking to capture the expressions as a list, before calling the `Procedure` constructor. + +* In `lispy.py` I made [changes and a pull request](https://github.com/norvig/pytudes/pull/106) to make it run on Python 3. + _Luciano Ramalho
June 29, 2021_ diff --git a/04-text-byte/charfinder/README.rst b/04-text-byte/charfinder/README.rst index 46a5d70..15a613d 100644 --- a/04-text-byte/charfinder/README.rst +++ b/04-text-byte/charfinder/README.rst @@ -58,7 +58,7 @@ Test ``find`` with single result:: Test ``find`` with two results:: - >>> find('chess', 'queen', last=0xFFFF) # doctest:+NORMALIZE_WHITESPACE + >>> find('chess', 'queen', end=0xFFFF) # doctest:+NORMALIZE_WHITESPACE U+2655 ♕ WHITE CHESS QUEEN U+265B ♛ BLACK CHESS QUEEN diff --git a/04-text-byte/charfinder/cf.py b/04-text-byte/charfinder/cf.py index 28f20d0..db982bc 100755 --- a/04-text-byte/charfinder/cf.py +++ b/04-text-byte/charfinder/cf.py @@ -2,11 +2,11 @@ import sys import unicodedata -FIRST, LAST = ord(' '), sys.maxunicode # <1> +START, END = ord(' '), sys.maxunicode + 1 # <1> -def find(*query_words, first=FIRST, last=LAST): # <2> +def find(*query_words, start=START, end=END): # <2> query = {w.upper() for w in query_words} # <3> - for code in range(first, last + 1): + for code in range(start, end): char = chr(code) # <4> name = unicodedata.name(char, None) # <5> if name and query.issubset(name.split()): # <6> diff --git a/05-data-classes/dataclass/club.py b/05-data-classes/dataclass/club.py index cd8ff46..7af49c8 100644 --- a/05-data-classes/dataclass/club.py +++ b/05-data-classes/dataclass/club.py @@ -1,9 +1,7 @@ from dataclasses import dataclass, field - @dataclass class ClubMember: - name: str guests: list = field(default_factory=list) diff --git a/05-data-classes/dataclass/club_wrong.py b/05-data-classes/dataclass/club_wrong.py index 3d73d6a..8521a9d 100644 --- a/05-data-classes/dataclass/club_wrong.py +++ b/05-data-classes/dataclass/club_wrong.py @@ -3,7 +3,6 @@ # tag::CLUBMEMBER[] @dataclass class ClubMember: - name: str guests: list = [] # end::CLUBMEMBER[] diff --git a/05-data-classes/dataclass/hackerclub.py b/05-data-classes/dataclass/hackerclub.py index 4d9112e..762c2cd 100644 --- a/05-data-classes/dataclass/hackerclub.py +++ b/05-data-classes/dataclass/hackerclub.py @@ -34,9 +34,7 @@ @dataclass class HackerClubMember(ClubMember): # <1> - all_handles = set() # <2> - handle: str = '' # <3> def __post_init__(self): diff --git a/05-data-classes/dataclass/hackerclub_annotated.py b/05-data-classes/dataclass/hackerclub_annotated.py index 5cf90fc..2394796 100644 --- a/05-data-classes/dataclass/hackerclub_annotated.py +++ b/05-data-classes/dataclass/hackerclub_annotated.py @@ -35,9 +35,7 @@ @dataclass class HackerClubMember(ClubMember): - all_handles: ClassVar[set[str]] = set() - handle: str = '' def __post_init__(self): diff --git a/05-data-classes/dataclass/resource.py b/05-data-classes/dataclass/resource.py index 5190055..f332a11 100644 --- a/05-data-classes/dataclass/resource.py +++ b/05-data-classes/dataclass/resource.py @@ -32,7 +32,7 @@ from datetime import date -class ResourceType(Enum): # <1> +class ResourceType(Enum): # <1> BOOK = auto() EBOOK = auto() VIDEO = auto() diff --git a/05-data-classes/meaning/demo_dc.py b/05-data-classes/meaning/demo_dc.py index 3cc45ce..fa45bb8 100644 --- a/05-data-classes/meaning/demo_dc.py +++ b/05-data-classes/meaning/demo_dc.py @@ -2,7 +2,6 @@ @dataclass class DemoDataClass: - a: int # <1> b: float = 1.1 # <2> c = 'spam' # <3> diff --git a/05-data-classes/meaning/demo_nt.py b/05-data-classes/meaning/demo_nt.py index 317fb82..8f52354 100644 --- a/05-data-classes/meaning/demo_nt.py +++ b/05-data-classes/meaning/demo_nt.py @@ -1,7 +1,6 @@ import typing class DemoNTClass(typing.NamedTuple): - a: int # <1> b: float = 1.1 # <2> c = 'spam' # <3> diff --git a/05-data-classes/meaning/demo_plain.py b/05-data-classes/meaning/demo_plain.py index 6376959..98c3e40 100644 --- a/05-data-classes/meaning/demo_plain.py +++ b/05-data-classes/meaning/demo_plain.py @@ -1,5 +1,4 @@ class DemoPlainClass: - a: int # <1> b: float = 1.1 # <2> c = 'spam' # <3> diff --git a/05-data-classes/typing_namedtuple/coordinates.py b/05-data-classes/typing_namedtuple/coordinates.py index 378a430..5e4d879 100644 --- a/05-data-classes/typing_namedtuple/coordinates.py +++ b/05-data-classes/typing_namedtuple/coordinates.py @@ -11,7 +11,6 @@ from typing import NamedTuple class Coordinate(NamedTuple): - lat: float lon: float diff --git a/05-data-classes/typing_namedtuple/coordinates2.py b/05-data-classes/typing_namedtuple/coordinates2.py index 2032311..efcd6be 100644 --- a/05-data-classes/typing_namedtuple/coordinates2.py +++ b/05-data-classes/typing_namedtuple/coordinates2.py @@ -13,8 +13,7 @@ from typing import NamedTuple class Coordinate(NamedTuple): - lat: float # <1> lon: float reference: str = 'WGS84' # <2> -# end::COORDINATE[] \ No newline at end of file +# end::COORDINATE[] diff --git a/05-data-classes/typing_namedtuple/nocheck_demo.py b/05-data-classes/typing_namedtuple/nocheck_demo.py index 8ca5dc1..43c1b96 100644 --- a/05-data-classes/typing_namedtuple/nocheck_demo.py +++ b/05-data-classes/typing_namedtuple/nocheck_demo.py @@ -1,7 +1,6 @@ import typing class Coordinate(typing.NamedTuple): - lat: float lon: float diff --git a/07-1class-func/clip.py b/07-1class-func/clip.py deleted file mode 100644 index 2f97c66..0000000 --- a/07-1class-func/clip.py +++ /dev/null @@ -1,48 +0,0 @@ -""" - >>> clip('banana split', 5) - 'banana' - >>> clip('banana split', 6) - 'banana' - >>> clip('banana split', 7) - 'banana' - >>> clip('banana split', 8) - 'banana' - >>> clip('banana split', 11) - 'banana' - >>> clip('banana split', 12) - 'banana split' - >>> clip('banana-split', 3) - 'banana-split' - -Jess' tests: - - >>> text = 'The quick brown fox jumps over the lazy dog.' - >>> clip14 = clip(text, max_len=14) - >>> clip14 - 'The quick' - >>> len(clip14) - 9 - >>> clip15 = clip(text, max_len=15) - >>> clip15 - 'The quick brown' - >>> len(clip15) - 15 - -""" - -# tag::CLIP[] -def clip(text, max_len=80): - """Return max_len characters clipped at space if possible""" - text = text.rstrip() - if len(text) <= max_len or ' ' not in text: - return text - end = len(text) - space_at = text.rfind(' ', 0, max_len + 1) - if space_at >= 0: - end = space_at - else: - space_at = text.find(' ', max_len) - if space_at >= 0: - end = space_at - return text[:end].rstrip() -# end::CLIP[] diff --git a/07-1class-func/clip_introspection.rst b/07-1class-func/clip_introspection.rst deleted file mode 100644 index 0b4334d..0000000 --- a/07-1class-func/clip_introspection.rst +++ /dev/null @@ -1,9 +0,0 @@ ->>> from clip import clip ->>> clip.__defaults__ -(80,) ->>> clip.__code__ # doctest: +ELLIPSIS - ->>> clip.__code__.co_varnames -('text', 'max_len', 'end', 'space_at') ->>> clip.__code__.co_argcount -2 diff --git a/07-1class-func/clip_signature.rst b/07-1class-func/clip_signature.rst deleted file mode 100644 index 9bc2dee..0000000 --- a/07-1class-func/clip_signature.rst +++ /dev/null @@ -1,12 +0,0 @@ ->>> from clip import clip ->>> from inspect import signature ->>> sig = signature(clip) ->>> sig - ->>> str(sig) -'(text, max_len=80)' ->>> for name, param in sig.parameters.items(): -... print(param.kind, ':', name, '=', param.default) -... -POSITIONAL_OR_KEYWORD : text = -POSITIONAL_OR_KEYWORD : max_len = 80 diff --git a/08-def-type-hints/RPN_calc/calc.py b/08-def-type-hints/RPN_calc/calc.py deleted file mode 100755 index 967b094..0000000 --- a/08-def-type-hints/RPN_calc/calc.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python3 - -import sys -from array import array -from typing import Mapping, MutableSequence, Callable, Iterable, Union, Any - - -OPERATORS: Mapping[str, Callable[[float, float], float]] = { - '+': lambda a, b: a + b, - '-': lambda a, b: a - b, - '*': lambda a, b: a * b, - '/': lambda a, b: a / b, - '^': lambda a, b: a ** b, -} - - -Stack = MutableSequence[float] - - -def parse_token(token: str) -> Union[str, float]: - try: - return float(token) - except ValueError: - return token - - -def evaluate(tokens: Iterable[str], stack: Stack) -> None: - for token in tokens: - atom = parse_token(token) - if isinstance(atom, float): - stack.append(atom) - else: # not float, must be operator - op = OPERATORS[atom] - x, y = stack.pop(), stack.pop() - result = op(y, x) - stack.append(result) - - -def display(s: Stack) -> str: - items = (repr(n) for n in s) - return ' │ '.join(items) + ' →' - - -def repl(input_fn: Callable[[Any], str] = input) -> None: - """Read-Eval-Print-Loop""" - - print('Use CTRL+C to quit.', file=sys.stderr) - stack: Stack = array('d') - - while True: - try: - line = input_fn('> ') # Read - except (EOFError, KeyboardInterrupt): - break - try: - evaluate(line.split(), stack) # Eval - except IndexError: - print('*** Not enough arguments.', file=sys.stderr) - except KeyError as exc: - print('*** Unknown operator:', exc.args[0], file=sys.stderr) - print(display(stack)) # Print - - print() - - -if __name__ == '__main__': - repl() diff --git a/08-def-type-hints/RPN_calc/calc_test.py b/08-def-type-hints/RPN_calc/calc_test.py deleted file mode 100644 index c2d144c..0000000 --- a/08-def-type-hints/RPN_calc/calc_test.py +++ /dev/null @@ -1,51 +0,0 @@ -from pytest import mark, approx # type: ignore - -from dialogue import Dialogue # type: ignore - -from calc import evaluate, repl, display, Stack - -TOLERANCE = .0001 - -@mark.parametrize("source, want", [ - ('2', 2), - ('2 3 +', 5), - ('5 3 -', 2), - ('3 5 * 2 +', 17), - ('2 3 4 5 * * *', 120), - ('1.1 1.1 1.1 + +', approx(3.3, TOLERANCE)), - ('100 32 - 5 * 9 /', approx(37.78, TOLERANCE)), -]) -def test_evaluate(source, want) -> None: - stack: Stack = [] - evaluate(source.split(), stack) - assert want == stack[-1] - - -@mark.parametrize("value, want", [ - ([], ' →'), - ([3.], '3.0 →'), - ([3., 4., 5.], '3.0 │ 4.0 │ 5.0 →'), -]) -def test_display(value, want) -> None: - assert want == display(value) - - -@mark.parametrize("session", [ - """ - > 3 - 3.0 → - """, - """ - > 3 5 6 - 3.0 │ 5.0 │ 6.0 → - > * - 3.0 │ 30.0 → - > - - -27.0 → - """, -]) -def test_repl(capsys, session) -> None: - dlg = Dialogue(session) - repl(dlg.fake_input) - captured = capsys.readouterr() - assert dlg.session.strip() == captured.out.strip() diff --git a/08-def-type-hints/charindex.py b/08-def-type-hints/charindex.py index 36d20e3..c368c37 100644 --- a/08-def-type-hints/charindex.py +++ b/08-def-type-hints/charindex.py @@ -15,7 +15,7 @@ import sys import re import unicodedata -from typing import Dict, Set, Iterator +from collections.abc import Iterator RE_WORD = re.compile(r'\w+') STOP_CODE = sys.maxunicode + 1 @@ -25,8 +25,8 @@ def tokenize(text: str) -> Iterator[str]: # <1> for match in RE_WORD.finditer(text): yield match.group().upper() -def name_index(start: int = 32, end: int = STOP_CODE) -> Dict[str, Set[str]]: - index: Dict[str, Set[str]] = {} # <2> +def name_index(start: int = 32, end: int = STOP_CODE) -> dict[str, set[str]]: + index: dict[str, set[str]] = {} # <2> for char in (chr(i) for i in range(start, end)): if name := unicodedata.name(char, ''): # <3> for word in tokenize(name): diff --git a/08-def-type-hints/colors.py b/08-def-type-hints/colors.py index b57413c..c0d0665 100644 --- a/08-def-type-hints/colors.py +++ b/08-def-type-hints/colors.py @@ -1,4 +1,4 @@ -from typing import Tuple, Mapping +from collections.abc import Mapping NAMES = { 'aqua': 65535, @@ -19,7 +19,7 @@ 'yellow': 16776960, } -def rgb2hex(color=Tuple[int, int, int]) -> str: +def rgb2hex(color: tuple[int, int, int]) -> str: if any(c not in range(256) for c in color): raise ValueError('Color components must be in range(256)') values = (f'{n % 256:02x}' for n in color) @@ -27,7 +27,7 @@ def rgb2hex(color=Tuple[int, int, int]) -> str: HEX_ERROR = "Color must use format '#0099ff', got: {!r}" -def hex2rgb(color=str) -> Tuple[int, int, int]: +def hex2rgb(color: str) -> tuple[int, int, int]: if len(color) != 7 or color[0] != '#': raise ValueError(HEX_ERROR.format(color)) try: diff --git a/08-def-type-hints/columnize.py b/08-def-type-hints/columnize.py index ef995ae..66b72d8 100644 --- a/08-def-type-hints/columnize.py +++ b/08-def-type-hints/columnize.py @@ -1,7 +1,7 @@ # tag::COLUMNIZE[] -from typing import Sequence, List, Tuple +from collections.abc import Sequence -def columnize(sequence: Sequence[str], num_columns: int = 0) -> List[Tuple[str, ...]]: +def columnize(sequence: Sequence[str], num_columns: int = 0) -> list[tuple[str, ...]]: if num_columns == 0: num_columns = round(len(sequence) ** .5) num_rows, reminder = divmod(len(sequence), num_columns) diff --git a/08-def-type-hints/columnize2.py b/08-def-type-hints/columnize2.py deleted file mode 100644 index 2524fd3..0000000 --- a/08-def-type-hints/columnize2.py +++ /dev/null @@ -1,35 +0,0 @@ -from typing import Sequence, List, Tuple, TypeVar - -T = TypeVar('T') - -def columnize(sequence: Sequence[T], num_columns: int = 0) -> List[Tuple[T, ...]]: - if num_columns == 0: - num_columns = round(len(sequence) ** .5) - num_rows, reminder = divmod(len(sequence), num_columns) - num_rows += bool(reminder) - return [tuple(sequence[i::num_rows]) for i in range(num_rows)] - - -def demo() -> None: - nato = ('Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India' - ' Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo' - ' Sierra Tango Uniform Victor Whiskey X-ray Yankee Zulu' - ).split() - - for line in columnize(nato): - for word in line: - print(f'{word:15}', end='') - print() - - print() - for length in range(2, 21, 6): - values = list(range(1, length + 1)) - for row in columnize(values): - for cell in row: - print(f'{cell:5}', end='') - print() - print() - - -if __name__ == '__main__': - demo() diff --git a/08-def-type-hints/columnize_alias.py b/08-def-type-hints/columnize_alias.py deleted file mode 100644 index b783469..0000000 --- a/08-def-type-hints/columnize_alias.py +++ /dev/null @@ -1,26 +0,0 @@ -from typing import Sequence, List, Tuple - -Row = Tuple[str, ...] - -def columnize(sequence: Sequence[str], num_columns: int) -> List[Row]: - if num_columns == 0: - num_columns = round(len(sequence) ** .5) - num_rows, reminder = divmod(len(sequence), num_columns) - num_rows += bool(reminder) - return [tuple(sequence[i::num_rows]) for i in range(num_rows)] - - -def demo() -> None: - nato = ('Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India' - ' Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo' - ' Sierra Tango Uniform Victor Whiskey X-ray Yankee Zulu' - ).split() - - for row in columnize(nato, 4): - for word in row: - print(f'{word:15}', end='') - print() - - -if __name__ == '__main__': - demo() diff --git a/08-def-type-hints/comparable/top.py b/08-def-type-hints/comparable/top.py index c552890..2851f62 100644 --- a/08-def-type-hints/comparable/top.py +++ b/08-def-type-hints/comparable/top.py @@ -20,11 +20,14 @@ """ # tag::TOP[] -from typing import TypeVar, Iterable, List +from collections.abc import Iterable +from typing import TypeVar + from comparable import SupportsLessThan LT = TypeVar('LT', bound=SupportsLessThan) -def top(series: Iterable[LT], length: int) -> List[LT]: - return sorted(series, reverse=True)[:length] +def top(series: Iterable[LT], length: int) -> list[LT]: + ordered = sorted(series, reverse=True) + return ordered[:length] # end::TOP[] diff --git a/08-def-type-hints/comparable/top_test.py b/08-def-type-hints/comparable/top_test.py index baff36b..c8c39ac 100644 --- a/08-def-type-hints/comparable/top_test.py +++ b/08-def-type-hints/comparable/top_test.py @@ -1,6 +1,11 @@ -from typing import Tuple, List, Iterator, TYPE_CHECKING -import pytest # type: ignore +# tag::TOP_IMPORT[] +from collections.abc import Iterator +from typing import TYPE_CHECKING # <1> + +import pytest + from top import top +# end::TOP_IMPORT[] @pytest.mark.parametrize('series, length, expected', [ ((1, 2, 3), 2, [3, 2]), @@ -8,9 +13,9 @@ ((3, 3, 3), 1, [3]), ]) def test_top( - series: Tuple[float, ...], + series: tuple[float, ...], length: int, - expected: List[float], + expected: list[float], ) -> None: result = top(series, length) assert expected == result @@ -18,13 +23,13 @@ def test_top( # tag::TOP_TEST[] def test_top_tuples() -> None: fruit = 'mango pear apple kiwi banana'.split() - series: Iterator[Tuple[int, str]] = ( + series: Iterator[tuple[int, str]] = ( # <2> (len(s), s) for s in fruit) length = 3 expected = [(6, 'banana'), (5, 'mango'), (5, 'apple')] result = top(series, length) - if TYPE_CHECKING: - reveal_type(series) + if TYPE_CHECKING: # <3> + reveal_type(series) # <4> reveal_type(expected) reveal_type(result) assert result == expected @@ -34,7 +39,7 @@ def test_top_objects_error() -> None: series = [object() for _ in range(4)] if TYPE_CHECKING: reveal_type(series) - with pytest.raises(TypeError) as exc: - top(series, 3) - assert "'<' not supported" in str(exc) + with pytest.raises(TypeError) as excinfo: + top(series, 3) # <5> + assert "'<' not supported" in str(excinfo.value) # end::TOP_TEST[] diff --git a/08-def-type-hints/coordinates/coordinates.py b/08-def-type-hints/coordinates/coordinates.py index d48b390..fb594eb 100644 --- a/08-def-type-hints/coordinates/coordinates.py +++ b/08-def-type-hints/coordinates/coordinates.py @@ -8,10 +8,10 @@ """ # tag::GEOHASH[] -from geolib import geohash as gh # type: ignore +from geolib import geohash as gh # type: ignore # <1> PRECISION = 9 -def geohash(lat_lon: tuple[float, float]) -> str: +def geohash(lat_lon: tuple[float, float]) -> str: # <2> return gh.encode(*lat_lon, PRECISION) # end::GEOHASH[] diff --git a/08-def-type-hints/coordinates/coordinates_named.py b/08-def-type-hints/coordinates/coordinates_named.py index 87cac72..76bd9bb 100644 --- a/08-def-type-hints/coordinates/coordinates_named.py +++ b/08-def-type-hints/coordinates/coordinates_named.py @@ -9,7 +9,7 @@ """ # tag::GEOHASH[] -from typing import Tuple, NamedTuple +from typing import NamedTuple from geolib import geohash as gh # type: ignore @@ -21,16 +21,21 @@ class Coordinate(NamedTuple): def geohash(lat_lon: Coordinate) -> str: return gh.encode(*lat_lon, PRECISION) +# end::GEOHASH[] -def display(lat_lon: Tuple[float, float]) -> str: +# tag::DISPLAY[] +def display(lat_lon: tuple[float, float]) -> str: lat, lon = lat_lon ns = 'N' if lat >= 0 else 'S' ew = 'E' if lon >= 0 else 'W' return f'{abs(lat):0.1f}°{ns}, {abs(lon):0.1f}°{ew}' - -# end::GEOHASH[] +# end::DISPLAY[] def demo(): shanghai = 31.2304, 121.4737 + print(display(shanghai)) s = geohash(shanghai) print(s) + +if __name__ == '__main__': + demo() diff --git a/08-def-type-hints/list.py b/08-def-type-hints/list.py deleted file mode 100644 index 6d122e7..0000000 --- a/08-def-type-hints/list.py +++ /dev/null @@ -1,10 +0,0 @@ -from typing import List, Tuple - -def tokenize(text: str) -> List[str]: - return text.upper().split() - -l: List[str] = [] -l.append(1) -print(l) - -t: Tuple[str, float] = ('São Paulo', 12_176_866) diff --git a/08-def-type-hints/messages/hints_1/messages.py b/08-def-type-hints/messages/hints_1/messages.py index c00e706..59b432e 100644 --- a/08-def-type-hints/messages/hints_1/messages.py +++ b/08-def-type-hints/messages/hints_1/messages.py @@ -5,16 +5,15 @@ >>> show_count(1, 'bird') '1 bird' >>> show_count(0, 'bird') -'no bird' +'no birds' # end::SHOW_COUNT_DOCTEST[] """ # tag::SHOW_COUNT[] def show_count(count: int, word: str) -> str: - if count == 0: - return f'no {word}' - elif count == 1: - return f'{count} {word}' - return f'{count} {word}s' + if count == 1: + return f'1 {word}' + count_str = str(count) if count else 'no' + return f'{count_str} {word}s' # end::SHOW_COUNT[] diff --git a/08-def-type-hints/messages/hints_1/messages_test.py b/08-def-type-hints/messages/hints_1/messages_test.py index 2a16f25..3688da1 100644 --- a/08-def-type-hints/messages/hints_1/messages_test.py +++ b/08-def-type-hints/messages/hints_1/messages_test.py @@ -7,11 +7,11 @@ (1, '1 part'), (2, '2 parts'), ]) -def test_show_count(qty, expected): +def test_show_count(qty: int, expected: str) -> None: got = show_count(qty, 'part') assert got == expected def test_show_count_zero(): got = show_count(0, 'part') - assert got == 'no part' + assert got == 'no parts' diff --git a/08-def-type-hints/messages/hints_2/messages.py b/08-def-type-hints/messages/hints_2/messages.py index c43d85f..fd2a331 100644 --- a/08-def-type-hints/messages/hints_2/messages.py +++ b/08-def-type-hints/messages/hints_2/messages.py @@ -4,21 +4,22 @@ >>> show_count(1, 'bird') '1 bird' >>> show_count(0, 'bird') -'no bird' +'no birds' >>> show_count(3, 'virus', 'viruses') '3 viruses' +>>> show_count(1, 'virus', 'viruses') +'1 virus' +>>> show_count(0, 'virus', 'viruses') +'no viruses' """ # tag::SHOW_COUNT[] def show_count(count: int, singular: str, plural: str = '') -> str: - if count == 0: - return f'no {singular}' - elif count == 1: + if count == 1: return f'1 {singular}' - else: - if plural: - return f'{count} {plural}' - else: - return f'{count} {singular}s' + count_str = str(count) if count else 'no' + if not plural: + plural = singular + 's' + return f'{count_str} {plural}' # end::SHOW_COUNT[] diff --git a/08-def-type-hints/messages/hints_2/messages_test.py b/08-def-type-hints/messages/hints_2/messages_test.py index f7b2fe1..2678d9b 100644 --- a/08-def-type-hints/messages/hints_2/messages_test.py +++ b/08-def-type-hints/messages/hints_2/messages_test.py @@ -1,4 +1,4 @@ -from pytest import mark # type: ignore +from pytest import mark from messages import show_count @@ -6,9 +6,9 @@ @mark.parametrize('qty, expected', [ (1, '1 part'), (2, '2 parts'), - (0, 'no part'), + (0, 'no parts'), ]) -def test_show_count(qty, expected): +def test_show_count(qty: int, expected: str) -> None: got = show_count(qty, 'part') assert got == expected @@ -17,9 +17,9 @@ def test_show_count(qty, expected): @mark.parametrize('qty, expected', [ (1, '1 child'), (2, '2 children'), - (0, 'no child'), + (0, 'no children'), ]) -def test_irregular(qty, expected) -> None: +def test_irregular(qty: int, expected: str) -> None: got = show_count(qty, 'child', 'children') assert got == expected # end::TEST_IRREGULAR[] diff --git a/08-def-type-hints/messages/no_hints/messages.py b/08-def-type-hints/messages/no_hints/messages.py index d7898c5..df037e7 100644 --- a/08-def-type-hints/messages/no_hints/messages.py +++ b/08-def-type-hints/messages/no_hints/messages.py @@ -5,16 +5,15 @@ >>> show_count(1, 'bird') '1 bird' >>> show_count(0, 'bird') -'no bird' +'no birds' # end::SHOW_COUNT_DOCTEST[] """ # tag::SHOW_COUNT[] def show_count(count, word): - if count == 0: - return f'no {word}' - elif count == 1: - return f'{count} {word}' - return f'{count} {word}s' + if count == 1: + return f'1 {word}' + count_str = str(count) if count else 'no' + return f'{count_str} {word}s' # end::SHOW_COUNT[] diff --git a/08-def-type-hints/messages/no_hints/messages_test.py b/08-def-type-hints/messages/no_hints/messages_test.py index a8ec100..09532d3 100644 --- a/08-def-type-hints/messages/no_hints/messages_test.py +++ b/08-def-type-hints/messages/no_hints/messages_test.py @@ -12,4 +12,4 @@ def test_show_count(qty, expected): def test_show_count_zero(): got = show_count(0, 'part') - assert got == 'no part' + assert got == 'no parts' diff --git a/08-def-type-hints/mode/mode_T.py b/08-def-type-hints/mode/mode_T.py deleted file mode 100644 index cf88bbb..0000000 --- a/08-def-type-hints/mode/mode_T.py +++ /dev/null @@ -1,26 +0,0 @@ -from collections import Counter -from typing import Iterable, TypeVar - -T = TypeVar('T') - -def mode(data: Iterable[T]) -> T: - data = iter(data) - pairs = Counter(data).most_common(1) - if len(pairs) == 0: - raise ValueError('no mode for empty data') - return pairs[0][0] - - -def demo() -> None: - from typing import TYPE_CHECKING - pop: list[set] = [set(), set()] - m = mode(pop) - if TYPE_CHECKING: - reveal_type(pop) - reveal_type(m) - print(pop) - print(repr(m), type(m)) - - -if __name__ == '__main__': - demo() diff --git a/08-def-type-hints/mode/mode_float.py b/08-def-type-hints/mode/mode_float.py index 0684202..79308be 100644 --- a/08-def-type-hints/mode/mode_float.py +++ b/08-def-type-hints/mode/mode_float.py @@ -1,6 +1,6 @@ # tag::MODE_FLOAT[] from collections import Counter -from typing import Iterable +from collections.abc import Iterable def mode(data: Iterable[float]) -> float: pairs = Counter(data).most_common(1) @@ -20,4 +20,4 @@ def demo() -> None: print(repr(m), type(m)) if __name__ == '__main__': - demo() \ No newline at end of file + demo() diff --git a/08-def-type-hints/mode/mode_hashable.py b/08-def-type-hints/mode/mode_hashable.py index aa7f313..57c3a30 100644 --- a/08-def-type-hints/mode/mode_hashable.py +++ b/08-def-type-hints/mode/mode_hashable.py @@ -1,6 +1,7 @@ # tag::MODE_HASHABLE_T[] from collections import Counter -from typing import Iterable, Hashable, TypeVar +from collections.abc import Iterable, Hashable +from typing import TypeVar HashableT = TypeVar('HashableT', bound=Hashable) diff --git a/08-def-type-hints/mode/mode_hashable_wrong.py b/08-def-type-hints/mode/mode_hashable_wrong.py deleted file mode 100644 index ff770a7..0000000 --- a/08-def-type-hints/mode/mode_hashable_wrong.py +++ /dev/null @@ -1,24 +0,0 @@ -# tag::MODE_FLOAT[] -from collections import Counter -from typing import Iterable, Hashable - -def mode(data: Iterable[Hashable]) -> Hashable: - data = iter(data) - pairs = Counter(data).most_common(1) - if len(pairs) == 0: - raise ValueError('no mode for empty data') - return pairs[0][0] -# end::MODE_FLOAT[] - -def demo() -> None: - import typing - pop = 'abracadabra' - m = mode(pop) - if typing.TYPE_CHECKING: - reveal_type(pop) - reveal_type(m) - print(pop) - print(m.upper(), type(m)) - -if __name__ == '__main__': - demo() \ No newline at end of file diff --git a/08-def-type-hints/mode/mode_number.py b/08-def-type-hints/mode/mode_number.py deleted file mode 100644 index 999387f..0000000 --- a/08-def-type-hints/mode/mode_number.py +++ /dev/null @@ -1,26 +0,0 @@ -from collections import Counter -from typing import Iterable, TypeVar -from decimal import Decimal -from fractions import Fraction - -NumberT = TypeVar('NumberT', float, Decimal, Fraction) - -def mode(data: Iterable[NumberT]) -> NumberT: - pairs = Counter(data).most_common(1) - if len(pairs) == 0: - raise ValueError('no mode for empty data') - return pairs[0][0] - - -def demo() -> None: - from typing import TYPE_CHECKING - pop = [Fraction(1, 2), Fraction(1, 3), Fraction(1, 4), Fraction(1, 2)] - m = mode(pop) - if TYPE_CHECKING: - reveal_type(pop) - reveal_type(m) - print(pop) - print(repr(m), type(m)) - -if __name__ == '__main__': - demo() \ No newline at end of file diff --git a/08-def-type-hints/messages/hints_1/mypy.ini b/08-def-type-hints/mypy.ini similarity index 50% rename from 08-def-type-hints/messages/hints_1/mypy.ini rename to 08-def-type-hints/mypy.ini index f658867..ab42819 100644 --- a/08-def-type-hints/messages/hints_1/mypy.ini +++ b/08-def-type-hints/mypy.ini @@ -1,6 +1,4 @@ [mypy] -python_version = 3.8 +python_version = 3.9 warn_unused_configs = True disallow_incomplete_defs = True -[mypy-pytest] -ignore_missing_imports = True diff --git a/08-def-type-hints/passdrill.py b/08-def-type-hints/passdrill.py deleted file mode 100755 index 83b1be1..0000000 --- a/08-def-type-hints/passdrill.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python3 - -"""passdrill: typing drills for practicing passphrases -""" - -import os -import sys -from base64 import b64encode, b64decode -from getpass import getpass -from hashlib import scrypt -from typing import Sequence, Tuple - -HASH_FILENAME = 'passdrill.hash' -HELP = 'Use -s to save passphrase hash for practice.' - - -def prompt() -> str: - print('WARNING: the passphrase WILL BE SHOWN so that you can check it!') - confirmed = '' - while confirmed != 'y': - passphrase = input('Type passphrase to hash (it will be echoed): ') - if passphrase in ('', 'q'): - print('ERROR: the passphrase cannot be empty or "q".') - continue - print(f'Passphrase to be hashed -> {passphrase}') - confirmed = input('Confirm (y/n): ').lower() - return passphrase - - -def crypto_hash(salt: bytes, passphrase: str) -> bytes: - octets = passphrase.encode('utf-8') - # Recommended parameters for interactive logins as of 2017: - # N=32768, r=8 and p=1 (https://godoc.org/golang.org/x/crypto/scrypt) - return scrypt(octets, salt=salt, n=32768, r=8, p=1, maxmem=2 ** 26) - - -def build_hash(passphrase: str) -> bytes: - salt = os.urandom(32) - payload = crypto_hash(salt, passphrase) - return b64encode(salt) + b':' + b64encode(payload) - - -def save_hash() -> None: - salted_hash = build_hash(prompt()) - with open(HASH_FILENAME, 'wb') as fp: - fp.write(salted_hash) - print(f'Passphrase hash saved to {HASH_FILENAME}') - - -def load_hash() -> Tuple[bytes, bytes]: - try: - with open(HASH_FILENAME, 'rb') as fp: - salted_hash = fp.read() - except FileNotFoundError: - print('ERROR: passphrase hash file not found.', HELP) - # "standard" exit status codes: - # https://stackoverflow.com/questions/1101957/are-there-any-standard-exit-status-codes-in-linux/40484670#40484670 - sys.exit(74) # input/output error - - salt, stored_hash = salted_hash.split(b':') - return b64decode(salt), b64decode(stored_hash) - - -def practice() -> None: - salt, stored_hash = load_hash() - print('Type q to end practice.') - turn = 0 - correct = 0 - while True: - turn += 1 - response = getpass(f'{turn}:') - if response == '': - print('Type q to quit.') - turn -= 1 # don't count this response - continue - elif response == 'q': - turn -= 1 # don't count this response - break - if crypto_hash(salt, response) == stored_hash: - correct += 1 - answer = 'OK' - else: - answer = 'wrong' - print(f' {answer}\thits={correct}\tmisses={turn-correct}') - - if turn: - print(f'\n{turn} turns. {correct / turn:.1%} correct.') - - -def main(argv: Sequence[str]) -> None: - if len(argv) < 2: - practice() - elif len(argv) == 2 and argv[1] == '-s': - save_hash() - else: - print('ERROR: invalid argument.', HELP) - sys.exit(2) # command line usage error - - -if __name__ == '__main__': - main(sys.argv) diff --git a/08-def-type-hints/replacer.py b/08-def-type-hints/replacer.py index 73ec77d..553a927 100644 --- a/08-def-type-hints/replacer.py +++ b/08-def-type-hints/replacer.py @@ -13,9 +13,9 @@ """ # tag::ZIP_REPLACE[] -from typing import Iterable, Tuple +from collections.abc import Iterable -FromTo = Tuple[str, str] # <1> +FromTo = tuple[str, str] # <1> def zip_replace(text: str, changes: Iterable[FromTo]) -> str: # <2> for from_, to in changes: diff --git a/08-def-type-hints/replacer2.py b/08-def-type-hints/replacer2.py deleted file mode 100644 index 33cdaeb..0000000 --- a/08-def-type-hints/replacer2.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -``zip_replace`` replaces multiple calls to ``str.replace``:: - - >>> changes = [ - ... ('(', ' ( '), - ... (')', ' ) '), - ... (' ', ' '), - ... ] - >>> expr = '(+ 2 (* 3 7))' - >>> zip_replace(expr, changes) - ' ( + 2 ( * 3 7 ) ) ' - -""" - -from typing import Iterable, NamedTuple - - -class FromTo(NamedTuple): - from_: str - to: str - - -def zip_replace(text: str, changes: Iterable[FromTo], count: int = -1) -> str: - for from_, to in changes: - text = text.replace(from_, to, count) - return text - - -def demo() -> None: - import doctest - failed, count = doctest.testmod() - print(f'{count-failed} of {count} doctests OK') - l33t = [FromTo(*p) for p in 'a4 e3 i1 o0'.split()] - text = 'mad skilled noob powned leet' - print(zip_replace(text, l33t)) - - -if __name__ == '__main__': - demo() diff --git a/08-def-type-hints/reveal_array.py b/08-def-type-hints/reveal_array.py deleted file mode 100644 index 149c85a..0000000 --- a/08-def-type-hints/reveal_array.py +++ /dev/null @@ -1,8 +0,0 @@ -from array import array -from typing import MutableSequence - -a = array('d') -reveal_type(a) -b: MutableSequence[float] = array('b') -reveal_type(b) - diff --git a/08-def-type-hints/sample.py b/08-def-type-hints/sample.py index 0e7e2e9..04b0319 100644 --- a/08-def-type-hints/sample.py +++ b/08-def-type-hints/sample.py @@ -1,10 +1,11 @@ # tag::SAMPLE[] +from collections.abc import Sequence from random import shuffle -from typing import Sequence, List, TypeVar +from typing import TypeVar T = TypeVar('T') -def sample(population: Sequence[T], size: int) -> List[T]: +def sample(population: Sequence[T], size: int) -> list[T]: if size < 1: raise ValueError('size must be >= 1') result = list(population) diff --git a/08-def-type-hints/typevar_bounded.py b/08-def-type-hints/typevar_bounded.py new file mode 100644 index 0000000..5c2adf0 --- /dev/null +++ b/08-def-type-hints/typevar_bounded.py @@ -0,0 +1,11 @@ +from typing import TypeVar, TYPE_CHECKING + +BT = TypeVar('BT', bound=float) + +def triple2(a: BT) -> BT: + return a * 3 + +res2 = triple2(2) + +if TYPE_CHECKING: + reveal_type(res2) diff --git a/08-def-type-hints/typevars_constrained.py b/08-def-type-hints/typevars_constrained.py index 2bfea13..8fb8d8e 100644 --- a/08-def-type-hints/typevars_constrained.py +++ b/08-def-type-hints/typevars_constrained.py @@ -7,7 +7,7 @@ def triple1(a: RT) -> RT: return a * 3 -res1 = triple1(1, 2) +res1 = triple1(2) if TYPE_CHECKING: reveal_type(res1) @@ -19,7 +19,7 @@ def triple1(a: RT) -> RT: def triple2(a: BT) -> BT: return a * 3 -res2 = triple2(1, 2) +res2 = triple2(2) if TYPE_CHECKING: reveal_type(res2) diff --git a/09-closure-deco/clockdeco.py b/09-closure-deco/clock/clockdeco.py similarity index 100% rename from 09-closure-deco/clockdeco.py rename to 09-closure-deco/clock/clockdeco.py diff --git a/09-closure-deco/clockdeco0.py b/09-closure-deco/clock/clockdeco0.py similarity index 100% rename from 09-closure-deco/clockdeco0.py rename to 09-closure-deco/clock/clockdeco0.py diff --git a/09-closure-deco/clockdeco_cls.py b/09-closure-deco/clock/clockdeco_cls.py similarity index 100% rename from 09-closure-deco/clockdeco_cls.py rename to 09-closure-deco/clock/clockdeco_cls.py diff --git a/09-closure-deco/clockdeco_demo.py b/09-closure-deco/clock/clockdeco_demo.py similarity index 90% rename from 09-closure-deco/clockdeco_demo.py rename to 09-closure-deco/clock/clockdeco_demo.py index 121b52f..bd41b5c 100644 --- a/09-closure-deco/clockdeco_demo.py +++ b/09-closure-deco/clock/clockdeco_demo.py @@ -1,17 +1,14 @@ import time -from clockdeco import clock - +from clockdeco0 import clock @clock def snooze(seconds): time.sleep(seconds) - @clock def factorial(n): return 1 if n < 2 else n*factorial(n-1) - if __name__ == '__main__': print('*' * 40, 'Calling snooze(.123)') snooze(.123) diff --git a/09-closure-deco/clockdeco_param.py b/09-closure-deco/clock/clockdeco_param.py similarity index 100% rename from 09-closure-deco/clockdeco_param.py rename to 09-closure-deco/clock/clockdeco_param.py diff --git a/09-closure-deco/clockdeco_param_demo1.py b/09-closure-deco/clock/clockdeco_param_demo1.py similarity index 100% rename from 09-closure-deco/clockdeco_param_demo1.py rename to 09-closure-deco/clock/clockdeco_param_demo1.py diff --git a/09-closure-deco/clockdeco_param_demo2.py b/09-closure-deco/clock/clockdeco_param_demo2.py similarity index 100% rename from 09-closure-deco/clockdeco_param_demo2.py rename to 09-closure-deco/clock/clockdeco_param_demo2.py diff --git a/10-dp-1class-func/classic_strategy.py b/10-dp-1class-func/classic_strategy.py index dc72e19..d796a79 100644 --- a/10-dp-1class-func/classic_strategy.py +++ b/10-dp-1class-func/classic_strategy.py @@ -6,20 +6,20 @@ >>> joe = Customer('John Doe', 0) # <1> >>> ann = Customer('Ann Smith', 1100) - >>> cart = [LineItem('banana', 4, .5), # <2> - ... LineItem('apple', 10, 1.5), - ... LineItem('watermelon', 5, 5.0)] + >>> cart = (LineItem('banana', 4, Decimal('.5')), # <2> + ... LineItem('apple', 10, Decimal('1.5')), + ... LineItem('watermelon', 5, Decimal(5))) >>> Order(joe, cart, FidelityPromo()) # <3> >>> Order(ann, cart, FidelityPromo()) # <4> - >>> banana_cart = [LineItem('banana', 30, .5), # <5> - ... LineItem('apple', 10, 1.5)] + >>> banana_cart = (LineItem('banana', 30, Decimal('.5')), # <5> + ... LineItem('apple', 10, Decimal('1.5'))) >>> Order(joe, banana_cart, BulkItemPromo()) # <6> - >>> big_cart = [LineItem(str(item_code), 1, 1.0) # <7> - ... for item_code in range(10)] - >>> Order(joe, big_cart, LargeOrderPromo()) # <8> + >>> long_cart = tuple(LineItem(str(sku), 1, Decimal(1)) # <7> + ... for sku in range(10)) + >>> Order(joe, long_cart, LargeOrderPromo()) # <8> >>> Order(joe, cart, LargeOrderPromo()) @@ -29,44 +29,37 @@ # tag::CLASSIC_STRATEGY[] from abc import ABC, abstractmethod -import typing -from typing import Sequence, Optional +from collections.abc import Sequence +from decimal import Decimal +from typing import NamedTuple, Optional -class Customer(typing.NamedTuple): +class Customer(NamedTuple): name: str fidelity: int -class LineItem: - def __init__(self, product: str, quantity: int, price: float): - self.product = product - self.quantity = quantity - self.price = price +class LineItem(NamedTuple): + product: str + quantity: int + price: Decimal - def total(self): + def total(self) -> Decimal: return self.price * self.quantity -class Order: # the Context - def __init__( - self, - customer: Customer, - cart: Sequence[LineItem], - promotion: Optional['Promotion'] = None, - ): - self.customer = customer - self.cart = list(cart) - self.promotion = promotion +class Order(NamedTuple): # the Context + customer: Customer + cart: Sequence[LineItem] + promotion: Optional['Promotion'] = None - def total(self) -> float: - if not hasattr(self, '__total'): - self.__total = sum(item.total() for item in self.cart) - return self.__total + def total(self) -> Decimal: + totals = (item.total() for item in self.cart) + return sum(totals, start=Decimal(0)) - def due(self) -> float: + def due(self) -> Decimal: if self.promotion is None: - discount = 0.0 + discount = Decimal(0) else: discount = self.promotion.discount(self) return self.total() - discount @@ -77,36 +70,37 @@ def __repr__(self): class Promotion(ABC): # the Strategy: an abstract base class @abstractmethod - def discount(self, order: Order) -> float: + def discount(self, order: Order) -> Decimal: """Return discount as a positive dollar amount""" class FidelityPromo(Promotion): # first Concrete Strategy """5% discount for customers with 1000 or more fidelity points""" - def discount(self, order: Order) -> float: - return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0 + def discount(self, order: Order) -> Decimal: + rate = Decimal('0.05') + if order.customer.fidelity >= 1000: + return order.total() * rate + return Decimal(0) class BulkItemPromo(Promotion): # second Concrete Strategy """10% discount for each LineItem with 20 or more units""" - def discount(self, order: Order) -> float: - discount = 0 + def discount(self, order: Order) -> Decimal: + discount = Decimal(0) for item in order.cart: if item.quantity >= 20: - discount += item.total() * 0.1 + discount += item.total() * Decimal('0.1') return discount class LargeOrderPromo(Promotion): # third Concrete Strategy """7% discount for orders with 10 or more distinct items""" - def discount(self, order: Order) -> float: + def discount(self, order: Order) -> Decimal: distinct_items = {item.product for item in order.cart} if len(distinct_items) >= 10: - return order.total() * 0.07 - return 0 - - + return order.total() * Decimal('0.07') + return Decimal(0) # end::CLASSIC_STRATEGY[] diff --git a/10-dp-1class-func/classic_strategy_test.py b/10-dp-1class-func/classic_strategy_test.py index 8735811..143cd0c 100644 --- a/10-dp-1class-func/classic_strategy_test.py +++ b/10-dp-1class-func/classic_strategy_test.py @@ -1,4 +1,4 @@ -from typing import List +from decimal import Decimal import pytest # type: ignore @@ -17,47 +17,49 @@ def customer_fidelity_1100() -> Customer: @pytest.fixture -def cart_plain() -> List[LineItem]: - return [ - LineItem('banana', 4, 0.5), - LineItem('apple', 10, 1.5), - LineItem('watermelon', 5, 5.0), - ] +def cart_plain() -> tuple[LineItem, ...]: + return ( + LineItem('banana', 4, Decimal('0.5')), + LineItem('apple', 10, Decimal('1.5')), + LineItem('watermelon', 5, Decimal('5.0')), + ) def test_fidelity_promo_no_discount(customer_fidelity_0, cart_plain) -> None: order = Order(customer_fidelity_0, cart_plain, FidelityPromo()) - assert order.total() == 42.0 - assert order.due() == 42.0 + assert order.total() == 42 + assert order.due() == 42 def test_fidelity_promo_with_discount(customer_fidelity_1100, cart_plain) -> None: order = Order(customer_fidelity_1100, cart_plain, FidelityPromo()) - assert order.total() == 42.0 - assert order.due() == 39.9 + assert order.total() == 42 + assert order.due() == Decimal('39.9') def test_bulk_item_promo_no_discount(customer_fidelity_0, cart_plain) -> None: order = Order(customer_fidelity_0, cart_plain, BulkItemPromo()) - assert order.total() == 42.0 - assert order.due() == 42.0 + assert order.total() == 42 + assert order.due() == 42 def test_bulk_item_promo_with_discount(customer_fidelity_0) -> None: - cart = [LineItem('banana', 30, 0.5), LineItem('apple', 10, 1.5)] + cart = [LineItem('banana', 30, Decimal('0.5')), + LineItem('apple', 10, Decimal('1.5'))] order = Order(customer_fidelity_0, cart, BulkItemPromo()) - assert order.total() == 30.0 - assert order.due() == 28.5 + assert order.total() == 30 + assert order.due() == Decimal('28.5') def test_large_order_promo_no_discount(customer_fidelity_0, cart_plain) -> None: order = Order(customer_fidelity_0, cart_plain, LargeOrderPromo()) - assert order.total() == 42.0 - assert order.due() == 42.0 + assert order.total() == 42 + assert order.due() == 42 def test_large_order_promo_with_discount(customer_fidelity_0) -> None: - cart = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)] + cart = [LineItem(str(item_code), 1, Decimal(1)) + for item_code in range(10)] order = Order(customer_fidelity_0, cart, LargeOrderPromo()) - assert order.total() == 10.0 - assert order.due() == 9.3 + assert order.total() == 10 + assert order.due() == Decimal('9.3') diff --git a/10-dp-1class-func/monkeytype/classic_strategy.py b/10-dp-1class-func/monkeytype/classic_strategy.py index 0057d96..670c4cb 100644 --- a/10-dp-1class-func/monkeytype/classic_strategy.py +++ b/10-dp-1class-func/monkeytype/classic_strategy.py @@ -17,9 +17,9 @@ ... LineItem('apple', 10, 1.5)] >>> Order(joe, banana_cart, BulkItemPromo()) # <6> - >>> long_order = [LineItem(str(item_code), 1, 1.0) # <7> + >>> long_cart = [LineItem(str(item_code), 1, 1.0) # <7> ... for item_code in range(10)] - >>> Order(joe, long_order, LargeOrderPromo()) # <8> + >>> Order(joe, long_cart, LargeOrderPromo()) # <8> >>> Order(joe, cart, LargeOrderPromo()) diff --git a/10-dp-1class-func/promotions.py b/10-dp-1class-func/promotions.py index 0f8a823..ee64b20 100644 --- a/10-dp-1class-func/promotions.py +++ b/10-dp-1class-func/promotions.py @@ -1,20 +1,25 @@ -def fidelity_promo(order): +from decimal import Decimal +from strategy import Order + +def fidelity_promo(order: Order) -> Decimal: # <3> """5% discount for customers with 1000 or more fidelity points""" - return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0 + if order.customer.fidelity >= 1000: + return order.total() * Decimal('0.05') + return Decimal(0) -def bulk_item_promo(order): +def bulk_item_promo(order: Order) -> Decimal: """10% discount for each LineItem with 20 or more units""" - discount = 0 + discount = Decimal(0) for item in order.cart: if item.quantity >= 20: - discount += item.total() * 0.1 + discount += item.total() * Decimal('0.1') return discount -def large_order_promo(order): +def large_order_promo(order: Order) -> Decimal: """7% discount for orders with 10 or more distinct items""" distinct_items = {item.product for item in order.cart} if len(distinct_items) >= 10: - return order.total() * 0.07 - return 0 + return order.total() * Decimal('0.07') + return Decimal(0) diff --git a/10-dp-1class-func/pytypes/classic_strategy.py b/10-dp-1class-func/pytypes/classic_strategy.py index d929ffa..710fd26 100644 --- a/10-dp-1class-func/pytypes/classic_strategy.py +++ b/10-dp-1class-func/pytypes/classic_strategy.py @@ -17,9 +17,9 @@ ... LineItem('apple', 10, 1.5)] >>> Order(joe, banana_cart, BulkItemPromo()) # <6> - >>> long_order = [LineItem(str(item_code), 1, 1.0) # <7> + >>> long_cart = [LineItem(str(item_code), 1, 1.0) # <7> ... for item_code in range(10)] - >>> Order(joe, long_order, LargeOrderPromo()) # <8> + >>> Order(joe, long_cart, LargeOrderPromo()) # <8> >>> Order(joe, cart, LargeOrderPromo()) diff --git a/10-dp-1class-func/requirements.txt b/10-dp-1class-func/requirements.txt index 1826922..4c4f0e1 100644 --- a/10-dp-1class-func/requirements.txt +++ b/10-dp-1class-func/requirements.txt @@ -1,13 +1,2 @@ -mypy==0.770 -mypy-extensions==0.4.3 -typed-ast==1.4.1 -typing-extensions==3.7.4.1 -attrs==19.3.0 -more-itertools==8.2.0 -packaging==20.3 -pluggy==0.13.1 -py==1.10.0 -pyparsing==2.4.6 -pytest==5.4.1 -six==1.14.0 -wcwidth==0.1.9 +mypy==0.910 +pytest==6.2.4 diff --git a/10-dp-1class-func/strategy.py b/10-dp-1class-func/strategy.py index 2ab49d1..89a93ba 100644 --- a/10-dp-1class-func/strategy.py +++ b/10-dp-1class-func/strategy.py @@ -6,20 +6,20 @@ >>> joe = Customer('John Doe', 0) # <1> >>> ann = Customer('Ann Smith', 1100) - >>> cart = [LineItem('banana', 4, .5), - ... LineItem('apple', 10, 1.5), - ... LineItem('watermelon', 5, 5.0)] + >>> cart = [LineItem('banana', 4, Decimal('.5')), + ... LineItem('apple', 10, Decimal('1.5')), + ... LineItem('watermelon', 5, Decimal(5))] >>> Order(joe, cart, fidelity_promo) # <2> >>> Order(ann, cart, fidelity_promo) - >>> banana_cart = [LineItem('banana', 30, .5), - ... LineItem('apple', 10, 1.5)] + >>> banana_cart = [LineItem('banana', 30, Decimal('.5')), + ... LineItem('apple', 10, Decimal('1.5'))] >>> Order(joe, banana_cart, bulk_item_promo) # <3> - >>> big_cart = [LineItem(str(item_code), 1, 1.0) + >>> long_cart = [LineItem(str(item_code), 1, Decimal(1)) ... for item_code in range(10)] - >>> Order(joe, big_cart, large_order_promo) + >>> Order(joe, long_cart, large_order_promo) >>> Order(joe, cart, large_order_promo) @@ -28,75 +28,71 @@ """ # tag::STRATEGY[] -import typing -from typing import Sequence, Optional, Callable +from collections.abc import Sequence +from dataclasses import dataclass +from decimal import Decimal +from typing import Optional, Callable, NamedTuple -class Customer(typing.NamedTuple): +class Customer(NamedTuple): name: str fidelity: int -class LineItem: - def __init__(self, product: str, quantity: int, price: float): - self.product = product - self.quantity = quantity - self.price = price +class LineItem(NamedTuple): + product: str + quantity: int + price: Decimal def total(self): return self.price * self.quantity - +@dataclass(frozen=True) class Order: # the Context - def __init__( - self, - customer: Customer, - cart: Sequence[LineItem], - promotion: Optional[Callable[['Order'], float]] = None, - ) -> None: - self.customer = customer - self.cart = list(cart) - self.promotion = promotion - - def total(self) -> float: - if not hasattr(self, '__total'): - self.__total = sum(item.total() for item in self.cart) - return self.__total - - def due(self) -> float: + customer: Customer + cart: Sequence[LineItem] + promotion: Optional[Callable[['Order'], Decimal]] = None # <1> + + def total(self) -> Decimal: + totals = (item.total() for item in self.cart) + return sum(totals, start=Decimal(0)) + + def due(self) -> Decimal: if self.promotion is None: - discount = 0.0 + discount = Decimal(0) else: - discount = self.promotion(self) # <1> + discount = self.promotion(self) # <2> return self.total() - discount def __repr__(self): return f'' -# <2> +# <3> -def fidelity_promo(order: Order) -> float: # <3> +def fidelity_promo(order: Order) -> Decimal: # <4> """5% discount for customers with 1000 or more fidelity points""" - return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0 + if order.customer.fidelity >= 1000: + return order.total() * Decimal('0.05') + return Decimal(0) -def bulk_item_promo(order: Order): +def bulk_item_promo(order: Order) -> Decimal: """10% discount for each LineItem with 20 or more units""" - discount = 0 + discount = Decimal(0) for item in order.cart: if item.quantity >= 20: - discount += item.total() * 0.1 + discount += item.total() * Decimal('0.1') return discount -def large_order_promo(order: Order): +def large_order_promo(order: Order) -> Decimal: """7% discount for orders with 10 or more distinct items""" distinct_items = {item.product for item in order.cart} if len(distinct_items) >= 10: - return order.total() * 0.07 - return 0 + return order.total() * Decimal('0.07') + return Decimal(0) # end::STRATEGY[] diff --git a/10-dp-1class-func/strategy_best.py b/10-dp-1class-func/strategy_best.py index a7c4d74..68cab48 100644 --- a/10-dp-1class-func/strategy_best.py +++ b/10-dp-1class-func/strategy_best.py @@ -3,19 +3,20 @@ # selecting best promotion from static list of functions """ + >>> from strategy import Customer, LineItem >>> joe = Customer('John Doe', 0) >>> ann = Customer('Ann Smith', 1100) - >>> cart = [LineItem('banana', 4, .5), - ... LineItem('apple', 10, 1.5), - ... LineItem('watermelon', 5, 5.0)] - >>> banana_cart = [LineItem('banana', 30, .5), - ... LineItem('apple', 10, 1.5)] - >>> big_cart = [LineItem(str(item_code), 1, 1.0) + >>> cart = [LineItem('banana', 4, Decimal('.5')), + ... LineItem('apple', 10, Decimal('1.5')), + ... LineItem('watermelon', 5, Decimal(5))] + >>> banana_cart = [LineItem('banana', 30, Decimal('.5')), + ... LineItem('apple', 10, Decimal('1.5'))] + >>> long_cart = [LineItem(str(item_code), 1, Decimal(1)) ... for item_code in range(10)] # tag::STRATEGY_BEST_TESTS[] - >>> Order(joe, big_cart, best_promo) # <1> + >>> Order(joe, long_cart, best_promo) # <1> >>> Order(joe, banana_cart, best_promo) # <2> @@ -25,7 +26,9 @@ # end::STRATEGY_BEST_TESTS[] """ -from strategy import Customer, LineItem, Order +from decimal import Decimal + +from strategy import Order from strategy import fidelity_promo, bulk_item_promo, large_order_promo # tag::STRATEGY_BEST[] @@ -33,9 +36,8 @@ promos = [fidelity_promo, bulk_item_promo, large_order_promo] # <1> -def best_promo(order) -> float: # <2> - """Select best discount available - """ +def best_promo(order: Order) -> Decimal: # <2> + """Compute the best discount available""" return max(promo(order) for promo in promos) # <3> diff --git a/10-dp-1class-func/strategy_best2.py b/10-dp-1class-func/strategy_best2.py index 62a993e..1c6ad9c 100644 --- a/10-dp-1class-func/strategy_best2.py +++ b/10-dp-1class-func/strategy_best2.py @@ -3,29 +3,31 @@ # selecting best promotion from current module globals """ + >>> from decimal import Decimal + >>> from strategy import Customer, LineItem, Order >>> joe = Customer('John Doe', 0) >>> ann = Customer('Ann Smith', 1100) - >>> cart = [LineItem('banana', 4, .5), - ... LineItem('apple', 10, 1.5), - ... LineItem('watermelon', 5, 5.0)] + >>> cart = [LineItem('banana', 4, Decimal('.5')), + ... LineItem('apple', 10, Decimal('1.5')), + ... LineItem('watermelon', 5, Decimal(5))] >>> Order(joe, cart, fidelity_promo) >>> Order(ann, cart, fidelity_promo) - >>> banana_cart = [LineItem('banana', 30, .5), - ... LineItem('apple', 10, 1.5)] + >>> banana_cart = [LineItem('banana', 30, Decimal('.5')), + ... LineItem('apple', 10, Decimal('1.5'))] >>> Order(joe, banana_cart, bulk_item_promo) - >>> long_order = [LineItem(str(item_code), 1, 1.0) + >>> long_cart = [LineItem(str(item_code), 1, Decimal(1)) ... for item_code in range(10)] - >>> Order(joe, long_order, large_order_promo) + >>> Order(joe, long_cart, large_order_promo) >>> Order(joe, cart, large_order_promo) # tag::STRATEGY_BEST_TESTS[] - >>> Order(joe, long_order, best_promo) + >>> Order(joe, long_cart, best_promo) >>> Order(joe, banana_cart, best_promo) @@ -35,78 +37,21 @@ # end::STRATEGY_BEST_TESTS[] """ -from collections import namedtuple - -Customer = namedtuple('Customer', 'name fidelity') - - -class LineItem: - def __init__(self, product, quantity, price): - self.product = product - self.quantity = quantity - self.price = price - - def total(self): - return self.price * self.quantity - - -class Order: # the Context - def __init__(self, customer, cart, promotion=None): - self.customer = customer - self.cart = list(cart) - self.promotion = promotion - - def total(self): - if not hasattr(self, '__total'): - self.__total = sum(item.total() for item in self.cart) - return self.__total - - def due(self): - if self.promotion is None: - discount = 0 - else: - discount = self.promotion(self) - return self.total() - discount - - def __repr__(self): - return f'' - - -def fidelity_promo(order): - """5% discount for customers with 1000 or more fidelity points""" - return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0 - - -def bulk_item_promo(order): - """10% discount for each LineItem with 20 or more units""" - discount = 0 - for item in order.cart: - if item.quantity >= 20: - discount += item.total() * 0.1 - return discount - - -def large_order_promo(order): - """7% discount for orders with 10 or more distinct items""" - distinct_items = {item.product for item in order.cart} - if len(distinct_items) >= 10: - return order.total() * 0.07 - return 0 - - # tag::STRATEGY_BEST2[] +from decimal import Decimal +from strategy import Order +from strategy import ( + fidelity_promo, bulk_item_promo, large_order_promo # <1> +) -promos = [ - globals()[name] - for name in globals() # <1> - if name.endswith('_promo') and name != 'best_promo' # <2> -] # <3> - +promos = [promo for name, promo in globals().items() # <2> + if name.endswith('_promo') and # <3> + name != 'best_promo' # <4> +] -def best_promo(order): - """Select best discount available - """ - return max(promo(order) for promo in promos) # <4> +def best_promo(order: Order) -> Decimal: # <5> + """Compute the best discount available""" + return max(promo(order) for promo in promos) # end::STRATEGY_BEST2[] diff --git a/10-dp-1class-func/strategy_best3.py b/10-dp-1class-func/strategy_best3.py index 39ce3bf..8b16ceb 100644 --- a/10-dp-1class-func/strategy_best3.py +++ b/10-dp-1class-func/strategy_best3.py @@ -3,30 +3,32 @@ # selecting best promotion from imported module """ + >>> from decimal import Decimal + >>> from strategy import Customer, LineItem, Order >>> from promotions import * >>> joe = Customer('John Doe', 0) >>> ann = Customer('Ann Smith', 1100) - >>> cart = [LineItem('banana', 4, .5), - ... LineItem('apple', 10, 1.5), - ... LineItem('watermelon', 5, 5.0)] + >>> cart = [LineItem('banana', 4, Decimal('.5')), + ... LineItem('apple', 10, Decimal('1.5')), + ... LineItem('watermelon', 5, Decimal(5))] >>> Order(joe, cart, fidelity_promo) >>> Order(ann, cart, fidelity_promo) - >>> banana_cart = [LineItem('banana', 30, .5), - ... LineItem('apple', 10, 1.5)] + >>> banana_cart = [LineItem('banana', 30, Decimal('.5')), + ... LineItem('apple', 10, Decimal('1.5'))] >>> Order(joe, banana_cart, bulk_item_promo) - >>> long_order = [LineItem(str(item_code), 1, 1.0) + >>> long_cart = [LineItem(str(item_code), 1, Decimal(1)) ... for item_code in range(10)] - >>> Order(joe, long_order, large_order_promo) + >>> Order(joe, long_cart, large_order_promo) >>> Order(joe, cart, large_order_promo) # tag::STRATEGY_BEST_TESTS[] - >>> Order(joe, long_order, best_promo) + >>> Order(joe, long_cart, best_promo) >>> Order(joe, banana_cart, best_promo) @@ -36,55 +38,20 @@ # end::STRATEGY_BEST_TESTS[] """ -from collections import namedtuple +# tag::STRATEGY_BEST3[] + +from decimal import Decimal import inspect +from strategy import Order import promotions -Customer = namedtuple('Customer', 'name fidelity') - - -class LineItem: - def __init__(self, product, quantity, price): - self.product = product - self.quantity = quantity - self.price = price - - def total(self): - return self.price * self.quantity - - -class Order: # the Context - def __init__(self, customer, cart, promotion=None): - self.customer = customer - self.cart = list(cart) - self.promotion = promotion - def total(self): - if not hasattr(self, '__total'): - self.__total = sum(item.total() for item in self.cart) - return self.__total +promos = [func for _, func in inspect.getmembers(promotions, inspect.isfunction)] - def due(self): - if self.promotion is None: - discount = 0 - else: - discount = self.promotion(self) - return self.total() - discount - def __repr__(self): - return f'' - - -# tag::STRATEGY_BEST3[] - -promos = [func for name, func in inspect.getmembers(promotions, inspect.isfunction)] - - -def best_promo(order): - """Select best discount available - """ +def best_promo(order: Order) -> Decimal: + """Compute the best discount available""" return max(promo(order) for promo in promos) - # end::STRATEGY_BEST3[] diff --git a/10-dp-1class-func/strategy_best4.py b/10-dp-1class-func/strategy_best4.py index 955ac3a..8e124bb 100644 --- a/10-dp-1class-func/strategy_best4.py +++ b/10-dp-1class-func/strategy_best4.py @@ -4,29 +4,32 @@ # registered by a decorator """ + >>> from decimal import Decimal + >>> from strategy import Customer, LineItem, Order + >>> from promotions import * >>> joe = Customer('John Doe', 0) >>> ann = Customer('Ann Smith', 1100) - >>> cart = [LineItem('banana', 4, .5), - ... LineItem('apple', 10, 1.5), - ... LineItem('watermelon', 5, 5.0)] - >>> Order(joe, cart, fidelity) + >>> cart = [LineItem('banana', 4, Decimal('.5')), + ... LineItem('apple', 10, Decimal('1.5')), + ... LineItem('watermelon', 5, Decimal(5))] + >>> Order(joe, cart, fidelity_promo) - >>> Order(ann, cart, fidelity) + >>> Order(ann, cart, fidelity_promo) - >>> banana_cart = [LineItem('banana', 30, .5), - ... LineItem('apple', 10, 1.5)] - >>> Order(joe, banana_cart, bulk_item) + >>> banana_cart = [LineItem('banana', 30, Decimal('.5')), + ... LineItem('apple', 10, Decimal('1.5'))] + >>> Order(joe, banana_cart, bulk_item_promo) - >>> long_order = [LineItem(str(item_code), 1, 1.0) + >>> long_cart = [LineItem(str(item_code), 1, Decimal(1)) ... for item_code in range(10)] - >>> Order(joe, long_order, large_order) + >>> Order(joe, long_cart, large_order_promo) - >>> Order(joe, cart, large_order) + >>> Order(joe, cart, large_order_promo) # tag::STRATEGY_BEST_TESTS[] - >>> Order(joe, long_order, best_promo) + >>> Order(joe, long_cart, best_promo) >>> Order(joe, banana_cart, best_promo) @@ -36,47 +39,16 @@ # end::STRATEGY_BEST_TESTS[] """ -from collections import namedtuple -from typing import Callable, List - -Customer = namedtuple('Customer', 'name fidelity') - - -class LineItem: - def __init__(self, product, quantity, price): - self.product = product - self.quantity = quantity - self.price = price - - def total(self): - return self.price * self.quantity - - -class Order: # the Context - def __init__(self, customer, cart, promotion=None): - self.customer = customer - self.cart = list(cart) - self.promotion = promotion - - def total(self): - if not hasattr(self, '__total'): - self.__total = sum(item.total() for item in self.cart) - return self.__total - - def due(self): - if self.promotion is None: - discount = 0 - else: - discount = self.promotion(self) - return self.total() - discount - - def __repr__(self): - return f'' +from decimal import Decimal +from typing import Callable +from strategy import Order # tag::STRATEGY_BEST4[] -Promotion = Callable[[Order], float] # <2> +Promotion = Callable[[Order], Decimal] + +promos: list[Promotion] = [] # <1> def promotion(promo: Promotion) -> Promotion: # <2> @@ -84,38 +56,35 @@ def promotion(promo: Promotion) -> Promotion: # <2> return promo -promos: List[Promotion] = [] # <1> +def best_promo(order: Order) -> Decimal: + """Compute the best discount available""" + return max(promo(order) for promo in promos) # <3> -@promotion # <3> -def fidelity(order: Order) -> float: +@promotion # <4> +def fidelity(order: Order) -> Decimal: """5% discount for customers with 1000 or more fidelity points""" - return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0 + if order.customer.fidelity >= 1000: + return order.total() * Decimal('0.05') + return Decimal(0) @promotion -def bulk_item(order: Order) -> float: +def bulk_item(order: Order) -> Decimal: """10% discount for each LineItem with 20 or more units""" - discount = 0 + discount = Decimal(0) for item in order.cart: if item.quantity >= 20: - discount += item.total() * 0.1 + discount += item.total() * Decimal('0.1') return discount @promotion -def large_order(order: Order) -> float: +def large_order(order: Order) -> Decimal: """7% discount for orders with 10 or more distinct items""" distinct_items = {item.product for item in order.cart} if len(distinct_items) >= 10: - return order.total() * 0.07 - return 0 - - -def best_promo(order: Order) -> float: # <4> - """Select best discount available - """ - return max(promo(order) for promo in promos) - + return order.total() * Decimal('0.07') + return Decimal(0) # end::STRATEGY_BEST4[] diff --git a/10-dp-1class-func/strategy_param.py b/10-dp-1class-func/strategy_param.py index 7318530..5490666 100644 --- a/10-dp-1class-func/strategy_param.py +++ b/10-dp-1class-func/strategy_param.py @@ -15,9 +15,9 @@ ... LineItem('apple', 10, 1.5)] >>> Order(joe, banana_cart, bulk_item_promo(10)) - >>> big_cart = [LineItem(str(item_code), 1, 1.0) + >>> long_cart = [LineItem(str(item_code), 1, 1.0) ... for item_code in range(10)] - >>> Order(joe, big_cart, LargeOrderPromo(7)) + >>> Order(joe, long_cart, LargeOrderPromo(7)) >>> Order(joe, cart, LargeOrderPromo(7)) diff --git a/10-dp-1class-func/strategy_param_test.py b/10-dp-1class-func/strategy_param_test.py index d550e2b..cfb965e 100644 --- a/10-dp-1class-func/strategy_param_test.py +++ b/10-dp-1class-func/strategy_param_test.py @@ -47,7 +47,7 @@ def test_large_order_promo_with_discount(customer_fidelity_0) -> None: assert order.due() == 9.3 -def test_general_discount(customer_fidelity_0, cart_plain) -> None: +def test_general_discount(customer_fidelity_1100, cart_plain) -> None: general_promo: Promotion = functools.partial(general_discount, 5) order = Order(customer_fidelity_1100, cart_plain, general_promo) assert order.total() == 42.0 diff --git a/10-dp-1class-func/strategy_test.py b/10-dp-1class-func/strategy_test.py index bf225ae..c47440e 100644 --- a/10-dp-1class-func/strategy_test.py +++ b/10-dp-1class-func/strategy_test.py @@ -1,4 +1,4 @@ -from typing import List +from decimal import Decimal import pytest # type: ignore @@ -17,47 +17,50 @@ def customer_fidelity_1100() -> Customer: @pytest.fixture -def cart_plain() -> List[LineItem]: - return [ - LineItem('banana', 4, 0.5), - LineItem('apple', 10, 1.5), - LineItem('watermelon', 5, 5.0), - ] +def cart_plain() -> tuple[LineItem, ...]: + return ( + LineItem('banana', 4, Decimal('0.5')), + LineItem('apple', 10, Decimal('1.5')), + LineItem('watermelon', 5, Decimal('5.0')), + ) def test_fidelity_promo_no_discount(customer_fidelity_0, cart_plain) -> None: order = Order(customer_fidelity_0, cart_plain, fidelity_promo) - assert order.total() == 42.0 - assert order.due() == 42.0 + assert order.total() == 42 + assert order.due() == 42 def test_fidelity_promo_with_discount(customer_fidelity_1100, cart_plain) -> None: order = Order(customer_fidelity_1100, cart_plain, fidelity_promo) - assert order.total() == 42.0 - assert order.due() == 39.9 + assert order.total() == 42 + assert order.due() == Decimal('39.9') def test_bulk_item_promo_no_discount(customer_fidelity_0, cart_plain) -> None: order = Order(customer_fidelity_0, cart_plain, bulk_item_promo) - assert order.total() == 42.0 - assert order.due() == 42.0 + assert order.total() == 42 + assert order.due() == 42 def test_bulk_item_promo_with_discount(customer_fidelity_0) -> None: - cart = [LineItem('banana', 30, 0.5), LineItem('apple', 10, 1.5)] + cart = [LineItem('banana', 30, Decimal('0.5')), + LineItem('apple', 10, Decimal('1.5'))] order = Order(customer_fidelity_0, cart, bulk_item_promo) - assert order.total() == 30.0 - assert order.due() == 28.5 + assert order.total() == 30 + assert order.due() == Decimal('28.5') def test_large_order_promo_no_discount(customer_fidelity_0, cart_plain) -> None: order = Order(customer_fidelity_0, cart_plain, large_order_promo) - assert order.total() == 42.0 - assert order.due() == 42.0 + assert order.total() == 42 + assert order.due() == 42 def test_large_order_promo_with_discount(customer_fidelity_0) -> None: - cart = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)] + + cart = [LineItem(str(item_code), 1, Decimal(1)) + for item_code in range(10)] order = Order(customer_fidelity_0, cart, large_order_promo) - assert order.total() == 10.0 - assert order.due() == 9.3 + assert order.total() == 10 + assert order.due() == Decimal('9.3') diff --git a/10-dp-1class-func/untyped/classic_strategy.py b/10-dp-1class-func/untyped/classic_strategy.py index b969780..61cccd5 100644 --- a/10-dp-1class-func/untyped/classic_strategy.py +++ b/10-dp-1class-func/untyped/classic_strategy.py @@ -17,9 +17,9 @@ ... LineItem('apple', 10, 1.5)] >>> Order(joe, banana_cart, BulkItemPromo()) # <6> - >>> long_order = [LineItem(str(item_code), 1, 1.0) # <7> + >>> long_cart = [LineItem(str(item_code), 1, 1.0) # <7> ... for item_code in range(10)] - >>> Order(joe, long_order, LargeOrderPromo()) # <8> + >>> Order(joe, long_cart, LargeOrderPromo()) # <8> >>> Order(joe, cart, LargeOrderPromo()) diff --git a/10-dp-1class-func/untyped/strategy.py b/10-dp-1class-func/untyped/strategy.py index 1f8ad4c..518c69d 100644 --- a/10-dp-1class-func/untyped/strategy.py +++ b/10-dp-1class-func/untyped/strategy.py @@ -17,9 +17,9 @@ ... LineItem('apple', 10, 1.5)] >>> Order(joe, banana_cart, bulk_item_promo) # <3> - >>> long_order = [LineItem(str(item_code), 1, 1.0) + >>> long_cart = [LineItem(str(item_code), 1, 1.0) ... for item_code in range(10)] - >>> Order(joe, long_order, large_order_promo) + >>> Order(joe, long_cart, large_order_promo) >>> Order(joe, cart, large_order_promo) diff --git a/10-dp-1class-func/untyped/strategy_best.py b/10-dp-1class-func/untyped/strategy_best.py index c0585f7..718b672 100644 --- a/10-dp-1class-func/untyped/strategy_best.py +++ b/10-dp-1class-func/untyped/strategy_best.py @@ -16,16 +16,16 @@ ... LineItem('apple', 10, 1.5)] >>> Order(joe, banana_cart, bulk_item_promo) - >>> long_order = [LineItem(str(item_code), 1, 1.0) + >>> long_cart = [LineItem(str(item_code), 1, 1.0) ... for item_code in range(10)] - >>> Order(joe, long_order, large_order_promo) + >>> Order(joe, long_cart, large_order_promo) >>> Order(joe, cart, large_order_promo) # tag::STRATEGY_BEST_TESTS[] - >>> Order(joe, long_order, best_promo) # <1> + >>> Order(joe, long_cart, best_promo) # <1> >>> Order(joe, banana_cart, best_promo) # <2> diff --git a/10-dp-1class-func/untyped/strategy_best2.py b/10-dp-1class-func/untyped/strategy_best2.py index 1f4700f..bfcd839 100644 --- a/10-dp-1class-func/untyped/strategy_best2.py +++ b/10-dp-1class-func/untyped/strategy_best2.py @@ -16,16 +16,16 @@ ... LineItem('apple', 10, 1.5)] >>> Order(joe, banana_cart, bulk_item_promo) - >>> long_order = [LineItem(str(item_code), 1, 1.0) + >>> long_cart = [LineItem(str(item_code), 1, 1.0) ... for item_code in range(10)] - >>> Order(joe, long_order, large_order_promo) + >>> Order(joe, long_cart, large_order_promo) >>> Order(joe, cart, large_order_promo) # tag::STRATEGY_BEST_TESTS[] - >>> Order(joe, long_order, best_promo) + >>> Order(joe, long_cart, best_promo) >>> Order(joe, banana_cart, best_promo) diff --git a/10-dp-1class-func/untyped/strategy_best3.py b/10-dp-1class-func/untyped/strategy_best3.py index 8d21ffc..f911ce3 100644 --- a/10-dp-1class-func/untyped/strategy_best3.py +++ b/10-dp-1class-func/untyped/strategy_best3.py @@ -17,16 +17,16 @@ ... LineItem('apple', 10, 1.5)] >>> Order(joe, banana_cart, bulk_item_promo) - >>> long_order = [LineItem(str(item_code), 1, 1.0) + >>> long_cart = [LineItem(str(item_code), 1, 1.0) ... for item_code in range(10)] - >>> Order(joe, long_order, large_order_promo) + >>> Order(joe, long_cart, large_order_promo) >>> Order(joe, cart, large_order_promo) # tag::STRATEGY_BEST_TESTS[] - >>> Order(joe, long_order, best_promo) + >>> Order(joe, long_cart, best_promo) >>> Order(joe, banana_cart, best_promo) diff --git a/10-dp-1class-func/untyped/strategy_best4.py b/10-dp-1class-func/untyped/strategy_best4.py index b523752..9573176 100644 --- a/10-dp-1class-func/untyped/strategy_best4.py +++ b/10-dp-1class-func/untyped/strategy_best4.py @@ -17,16 +17,16 @@ ... LineItem('apple', 10, 1.5)] >>> Order(joe, banana_cart, bulk_item) - >>> long_order = [LineItem(str(item_code), 1, 1.0) + >>> long_cart = [LineItem(str(item_code), 1, 1.0) ... for item_code in range(10)] - >>> Order(joe, long_order, large_order) + >>> Order(joe, long_cart, large_order) >>> Order(joe, cart, large_order) # tag::STRATEGY_BEST_TESTS[] - >>> Order(joe, long_order, best_promo) + >>> Order(joe, long_cart, best_promo) >>> Order(joe, banana_cart, best_promo) diff --git a/10-dp-1class-func/untyped/strategy_param.py b/10-dp-1class-func/untyped/strategy_param.py index d5cc931..ab07132 100644 --- a/10-dp-1class-func/untyped/strategy_param.py +++ b/10-dp-1class-func/untyped/strategy_param.py @@ -15,9 +15,9 @@ ... LineItem('apple', 10, 1.5)] >>> Order(joe, banana_cart, bulk_item_promo(10)) - >>> long_order = [LineItem(str(item_code), 1, 1.0) + >>> long_cart = [LineItem(str(item_code), 1, 1.0) ... for item_code in range(10)] - >>> Order(joe, long_order, large_order_promo(7)) + >>> Order(joe, long_cart, large_order_promo(7)) >>> Order(joe, cart, large_order_promo(7)) diff --git a/10-dp-1class-func/untyped/strategy_param2.py b/10-dp-1class-func/untyped/strategy_param2.py index 625bbca..332f49d 100644 --- a/10-dp-1class-func/untyped/strategy_param2.py +++ b/10-dp-1class-func/untyped/strategy_param2.py @@ -15,9 +15,9 @@ ... LineItem('apple', 10, 1.5)] >>> Order(joe, banana_cart, BulkItemPromo(10)) - >>> long_order = [LineItem(str(item_code), 1, 1.0) + >>> long_cart = [LineItem(str(item_code), 1, 1.0) ... for item_code in range(10)] - >>> Order(joe, long_order, LargeOrderPromo(7)) + >>> Order(joe, long_cart, LargeOrderPromo(7)) >>> Order(joe, cart, LargeOrderPromo(7)) diff --git a/11-pythonic-obj/mem_test.py b/11-pythonic-obj/mem_test.py index 0d745f2..12fc54e 100644 --- a/11-pythonic-obj/mem_test.py +++ b/11-pythonic-obj/mem_test.py @@ -4,20 +4,25 @@ NUM_VECTORS = 10**7 +module = None if len(sys.argv) == 2: module_name = sys.argv[1].replace('.py', '') module = importlib.import_module(module_name) else: print(f'Usage: {sys.argv[0]} ') - sys.exit(2) # command line usage error -fmt = 'Selected Vector2d type: {.__name__}.{.__name__}' -print(fmt.format(module, module.Vector2d)) +if module is None: + print('Running test with built-in `complex`') + cls = complex +else: + fmt = 'Selected Vector2d type: {.__name__}.{.__name__}' + print(fmt.format(module, module.Vector2d)) + cls = module.Vector2d mem_init = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss -print(f'Creating {NUM_VECTORS:,} Vector2d instances') +print(f'Creating {NUM_VECTORS:,} {cls.__qualname__!r} instances') -vectors = [module.Vector2d(3.0, 4.0) for i in range(NUM_VECTORS)] +vectors = [cls(3.0, 4.0) for i in range(NUM_VECTORS)] mem_final = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss print(f'Initial RAM usage: {mem_init:14,}') diff --git a/11-pythonic-obj/patterns.py b/11-pythonic-obj/patterns.py new file mode 100644 index 0000000..9317a4c --- /dev/null +++ b/11-pythonic-obj/patterns.py @@ -0,0 +1,53 @@ +from vector2d_v3 import Vector2d + +# tag::KEYWORD_PATTERNS[] +def keyword_pattern_demo(v: Vector2d) -> None: + match v: + case Vector2d(x=0, y=0): + print(f'{v!r} is null') + case Vector2d(x=0): + print(f'{v!r} is vertical') + case Vector2d(y=0): + print(f'{v!r} is horizontal') + case Vector2d(x=x, y=y) if x==y: + print(f'{v!r} is diagonal') + case _: + print(f'{v!r} is awesome') +# end::KEYWORD_PATTERNS[] + +# tag::POSITIONAL_PATTERNS[] +def positional_pattern_demo(v: Vector2d) -> None: + match v: + case Vector2d(0, 0): + print(f'{v!r} is null') + case Vector2d(0): + print(f'{v!r} is vertical') + case Vector2d(_, 0): + print(f'{v!r} is horizontal') + case Vector2d(x, y) if x==y: + print(f'{v!r} is diagonal') + case _: + print(f'{v!r} is awesome') +# end::POSITIONAL_PATTERNS[] + + +def main(): + vectors = ( + Vector2d(1, 1), + Vector2d(0, 1), + Vector2d(1, 0), + Vector2d(1, 2), + Vector2d(0, 0), + ) + print('KEYWORD PATTERNS:') + for vector in vectors: + keyword_pattern_demo(vector) + + print('POSITIONAL PATTERNS:') + for vector in vectors: + positional_pattern_demo(vector) + + + +if __name__ == '__main__': + main() diff --git a/11-pythonic-obj/slots.rst b/11-pythonic-obj/slots.rst new file mode 100644 index 0000000..351c9de --- /dev/null +++ b/11-pythonic-obj/slots.rst @@ -0,0 +1,52 @@ +# tag::PIXEL[] +>>> class Pixel: +... __slots__ = ('x', 'y') # <1> +... +>>> p = Pixel() # <2> +>>> p.__dict__ # <3> +Traceback (most recent call last): + ... +AttributeError: 'Pixel' object has no attribute '__dict__' +>>> p.x = 10 # <4> +>>> p.y = 20 +>>> p.color = 'red' # <5> +Traceback (most recent call last): + ... +AttributeError: 'Pixel' object has no attribute 'color' + +# end::PIXEL[] + +# tag::OPEN_PIXEL[] +>>> class OpenPixel(Pixel): # <1> +... pass +... +>>> op = OpenPixel() +>>> op.__dict__ # <2> +{} +>>> op.x = 8 # <3> +>>> op.__dict__ # <4> +{} +>>> op.x # <5> +8 +>>> op.color = 'green' # <6> +>>> op.__dict__ # <7> +{'color': 'green'} + +# end::OPEN_PIXEL[] + +# tag::COLOR_PIXEL[] +>>> class ColorPixel(Pixel): +... __slots__ = ('color',) # <1> +>>> cp = ColorPixel() +>>> cp.__dict__ # <2> +Traceback (most recent call last): + ... +AttributeError: 'ColorPixel' object has no attribute '__dict__' +>>> cp.x = 2 +>>> cp.color = 'blue' # <3> +>>> cp.flavor = 'banana' +Traceback (most recent call last): + ... +AttributeError: 'ColorPixel' object has no attribute 'flavor' + +# end::COLOR_PIXEL[] diff --git a/11-pythonic-obj/vector2d_v3.py b/11-pythonic-obj/vector2d_v3.py index 376a8a4..9ee716c 100644 --- a/11-pythonic-obj/vector2d_v3.py +++ b/11-pythonic-obj/vector2d_v3.py @@ -72,7 +72,7 @@ >>> v1.x = 123 Traceback (most recent call last): ... - AttributeError: can't set attribute + AttributeError: can't set attribute 'x' Tests of hashing: @@ -90,6 +90,8 @@ import math class Vector2d: + __match_args__ = ('x', 'y') + typecode = 'd' def __init__(self, x, y): diff --git a/11-pythonic-obj/vector2d_v3_prophash.py b/11-pythonic-obj/vector2d_v3_prophash.py index 3552530..6d7dceb 100644 --- a/11-pythonic-obj/vector2d_v3_prophash.py +++ b/11-pythonic-obj/vector2d_v3_prophash.py @@ -72,7 +72,7 @@ >>> v1.x = 123 Traceback (most recent call last): ... - AttributeError: can't set attribute + AttributeError: can't set attribute 'x' # end::VECTOR2D_V3_HASH_DEMO[] @@ -112,7 +112,7 @@ def y(self): def __iter__(self): return (i for i in (self.x, self.y)) # <6> - # remaining methods follow (omitted in book listing) + # remaining methods: same as previous Vector2d # end::VECTOR2D_V3_PROP[] def __repr__(self): diff --git a/11-pythonic-obj/vector2d_v3_slots.py b/11-pythonic-obj/vector2d_v3_slots.py index 20b6da4..c48fb5b 100644 --- a/11-pythonic-obj/vector2d_v3_slots.py +++ b/11-pythonic-obj/vector2d_v3_slots.py @@ -72,7 +72,7 @@ >>> v1.x = 123 Traceback (most recent call last): ... - AttributeError: can't set attribute + AttributeError: can't set attribute 'x' Tests of hashing: @@ -90,11 +90,10 @@ # tag::VECTOR2D_V3_SLOTS[] class Vector2d: - __slots__ = ('__x', '__y') + __match_args__ = ('x', 'y') # <1> + __slots__ = ('__x', '__y') # <2> typecode = 'd' - - # methods follow (omitted in book listing) # end::VECTOR2D_V3_SLOTS[] def __init__(self, x, y): diff --git a/12-seq-hacking/vector_v3.py b/12-seq-hacking/vector_v3.py index 6ee18b5..c1f7859 100644 --- a/12-seq-hacking/vector_v3.py +++ b/12-seq-hacking/vector_v3.py @@ -199,15 +199,17 @@ def __getitem__(self, key): return self._components[index] # tag::VECTOR_V3_GETATTR[] - shortcut_names = 'xyzt' + __match_args__ = ('x', 'y', 'z', 't') # <1> def __getattr__(self, name): - cls = type(self) # <1> - if len(name) == 1: # <2> - pos = cls.shortcut_names.find(name) # <3> - if 0 <= pos < len(self._components): # <4> - return self._components[pos] - msg = f'{cls.__name__!r} object has no attribute {name!r}' # <5> + cls = type(self) # <2> + try: + pos = cls.__match_args__.index(name) # <3> + except ValueError: # <4> + pos = -1 + if 0 <= pos < len(self._components): # <5> + return self._components[pos] + msg = f'{cls.__name__!r} object has no attribute {name!r}' # <6> raise AttributeError(msg) # end::VECTOR_V3_GETATTR[] @@ -215,8 +217,8 @@ def __getattr__(self, name): def __setattr__(self, name, value): cls = type(self) if len(name) == 1: # <1> - if name in cls.shortcut_names: # <2> - error = 'read-only attribute {attr_name!r}' + if name in cls.__match_args__: # <2> + error = 'readonly attribute {attr_name!r}' elif name.islower(): # <3> error = "can't set attributes 'a' to 'z' in {cls_name!r}" else: diff --git a/12-seq-hacking/vector_v4.py b/12-seq-hacking/vector_v4.py index 95530eb..856f338 100644 --- a/12-seq-hacking/vector_v4.py +++ b/12-seq-hacking/vector_v4.py @@ -199,14 +199,16 @@ def __getitem__(self, key): index = operator.index(key) return self._components[index] - shortcut_names = 'xyzt' + __match_args__ = ('x', 'y', 'z', 't') def __getattr__(self, name): cls = type(self) - if len(name) == 1: - pos = cls.shortcut_names.find(name) - if 0 <= pos < len(self._components): - return self._components[pos] + try: + pos = cls.__match_args__.index(name) + except ValueError: + pos = -1 + if 0 <= pos < len(self._components): + return self._components[pos] msg = f'{cls.__name__!r} object has no attribute {name!r}' raise AttributeError(msg) diff --git a/12-seq-hacking/vector_v5.py b/12-seq-hacking/vector_v5.py index ebbd523..09b3044 100644 --- a/12-seq-hacking/vector_v5.py +++ b/12-seq-hacking/vector_v5.py @@ -242,14 +242,16 @@ def __getitem__(self, key): index = operator.index(key) return self._components[index] - shortcut_names = 'xyzt' + __match_args__ = ('x', 'y', 'z', 't') def __getattr__(self, name): cls = type(self) - if len(name) == 1: - pos = cls.shortcut_names.find(name) - if 0 <= pos < len(self._components): - return self._components[pos] + try: + pos = cls.__match_args__.index(name) + except ValueError: + pos = -1 + if 0 <= pos < len(self._components): + return self._components[pos] msg = f'{cls.__name__!r} object has no attribute {name!r}' raise AttributeError(msg) diff --git a/13-protocol-abc/frenchdeck2.py b/13-protocol-abc/frenchdeck2.py index e3ccc2c..612581d 100644 --- a/13-protocol-abc/frenchdeck2.py +++ b/13-protocol-abc/frenchdeck2.py @@ -1,8 +1,8 @@ -import collections +from collections import namedtuple, abc -Card = collections.namedtuple('Card', ['rank', 'suit']) +Card = namedtuple('Card', ['rank', 'suit']) -class FrenchDeck2(collections.MutableSequence): +class FrenchDeck2(abc.MutableSequence): ranks = [str(n) for n in range(2, 11)] + list('JQKA') suits = 'spades diamonds clubs hearts'.split() diff --git a/13-protocol-abc/typing/randompick_test.py b/13-protocol-abc/typing/randompick_test.py index f090022..115c4d3 100644 --- a/13-protocol-abc/typing/randompick_test.py +++ b/13-protocol-abc/typing/randompick_test.py @@ -12,14 +12,14 @@ def pick(self) -> Any: # <3> return self._items.pop() def test_isinstance() -> None: # <4> - popper = SimplePicker([1]) - assert isinstance(popper, RandomPicker) + popper: RandomPicker = SimplePicker([1]) # <5> + assert isinstance(popper, RandomPicker) # <6> -def test_item_type() -> None: # <5> +def test_item_type() -> None: # <7> items = [1, 2] popper = SimplePicker(items) item = popper.pick() assert item in items if TYPE_CHECKING: - reveal_type(item) # <6> + reveal_type(item) # <8> assert isinstance(item, int) diff --git a/13-protocol-abc/typing/vector2d_v4.py b/13-protocol-abc/typing/vector2d_v4.py index 1e9d4ac..deaa824 100644 --- a/13-protocol-abc/typing/vector2d_v4.py +++ b/13-protocol-abc/typing/vector2d_v4.py @@ -168,5 +168,5 @@ def __complex__(self): @classmethod def fromcomplex(cls, datum): - return Vector2d(datum.real, datum.imag) # <1> + return cls(datum.real, datum.imag) # <1> # end::VECTOR2D_V4_COMPLEX[] diff --git a/13-protocol-abc/typing/vector2d_v5.py b/13-protocol-abc/typing/vector2d_v5.py index ceda21c..378b826 100644 --- a/13-protocol-abc/typing/vector2d_v5.py +++ b/13-protocol-abc/typing/vector2d_v5.py @@ -170,5 +170,5 @@ def __complex__(self) -> complex: # <2> @classmethod def fromcomplex(cls, datum: SupportsComplex) -> Vector2d: # <3> c = complex(datum) # <4> - return Vector2d(c.real, c.imag) + return cls(c.real, c.imag) # end::VECTOR2D_V5_COMPLEX[] diff --git a/14-inheritance/uppermixin.py b/14-inheritance/uppermixin.py index d86ecea..12c4a17 100644 --- a/14-inheritance/uppermixin.py +++ b/14-inheritance/uppermixin.py @@ -1,10 +1,39 @@ -"""UpperDict uppercases all string keys. +""" +Short demos +=========== -Test for initializer. `str` keys are uppercased:: +``UpperDict`` behaves like a case-insensitive mapping`:: - >>> d = UpperDict([('a', 'letter A'), ('B', 'letter B'), (2, 'digit two')]) +# tag::UPPERDICT_DEMO[] + >>> d = UpperDict([('a', 'letter A'), (2, 'digit two')]) >>> list(d.keys()) - ['A', 'B', 2] + ['A', 2] + >>> d['b'] = 'letter B' + >>> 'b' in d + True + >>> d['a'], d.get('B') + ('letter A', 'letter B') + >>> list(d.keys()) + ['A', 2, 'B'] + +# end::UPPERDICT_DEMO[] + +And ``UpperCounter`` is also case-insensitive:: + +# tag::UPPERCOUNTER_DEMO[] + >>> c = UpperCounter('BaNanA') + >>> c.most_common() + [('A', 3), ('N', 2), ('B', 1)] + +# end::UPPERCOUNTER_DEMO[] + +Detailed tests +============== + +UpperDict uppercases all string keys. + + >>> d = UpperDict([('a', 'letter A'), ('B', 'letter B'), (2, 'digit two')]) + Tests for item retrieval using `d[key]` notation:: @@ -82,14 +111,12 @@ # tag::UPPERCASE_MIXIN[] import collections - def _upper(key): # <1> try: return key.upper() except AttributeError: return key - class UpperCaseMixin: # <2> def __setitem__(self, key, item): super().__setitem__(_upper(key), item) @@ -108,8 +135,6 @@ def __contains__(self, key): class UpperDict(UpperCaseMixin, collections.UserDict): # <1> pass - class UpperCounter(UpperCaseMixin, collections.Counter): # <2> """Specialized 'Counter' that uppercases string keys""" # <3> - # end::UPPERDICT[] diff --git a/15-more-types/protocol/abs_demo.py b/15-more-types/protocol/abs_demo.py index 9cb7f80..6af78ea 100755 --- a/15-more-types/protocol/abs_demo.py +++ b/15-more-types/protocol/abs_demo.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +# tag::ABS_DEMO[] import math from typing import NamedTuple, SupportsAbs @@ -30,3 +31,4 @@ def is_unit(v: SupportsAbs[float]) -> bool: # <2> assert is_unit(v4) print('OK') +# end::ABS_DEMO[] diff --git a/16-op-overloading/vector2d_v3.py b/16-op-overloading/vector2d_v3.py index 5812dcf..9ee716c 100644 --- a/16-op-overloading/vector2d_v3.py +++ b/16-op-overloading/vector2d_v3.py @@ -1,5 +1,5 @@ """ -A 2-dimensional vector class +A two-dimensional vector class >>> v1 = Vector2d(3, 4) >>> print(v1.x, v1.y) @@ -72,7 +72,7 @@ >>> v1.x = 123 Traceback (most recent call last): ... - AttributeError: can't set attribute + AttributeError: can't set attribute 'x' Tests of hashing: @@ -81,7 +81,7 @@ >>> v2 = Vector2d(3.1, 4.2) >>> hash(v1), hash(v2) (7, 384307168202284039) - >>> len(set([v1, v2])) + >>> len({v1, v2}) 2 """ @@ -90,6 +90,8 @@ import math class Vector2d: + __match_args__ = ('x', 'y') + typecode = 'd' def __init__(self, x, y): diff --git a/16-op-overloading/vector_v6.py b/16-op-overloading/vector_v6.py index 7154851..6ae4dcf 100644 --- a/16-op-overloading/vector_v6.py +++ b/16-op-overloading/vector_v6.py @@ -305,14 +305,16 @@ def __getitem__(self, key): index = operator.index(key) return self._components[index] - shortcut_names = 'xyzt' + __match_args__ = ('x', 'y', 'z', 't') def __getattr__(self, name): cls = type(self) - if len(name) == 1: - pos = cls.shortcut_names.find(name) - if 0 <= pos < len(self._components): - return self._components[pos] + try: + pos = cls.__match_args__.index(name) + except ValueError: + pos = -1 + if 0 <= pos < len(self._components): + return self._components[pos] msg = f'{cls.__name__!r} object has no attribute {name!r}' raise AttributeError(msg) diff --git a/16-op-overloading/vector_v7.py b/16-op-overloading/vector_v7.py index e59c896..953622e 100644 --- a/16-op-overloading/vector_v7.py +++ b/16-op-overloading/vector_v7.py @@ -355,14 +355,16 @@ def __getitem__(self, key): index = operator.index(key) return self._components[index] - shortcut_names = 'xyzt' + __match_args__ = ('x', 'y', 'z', 't') def __getattr__(self, name): cls = type(self) - if len(name) == 1: - pos = cls.shortcut_names.find(name) - if 0 <= pos < len(self._components): - return self._components[pos] + try: + pos = cls.__match_args__.index(name) + except ValueError: + pos = -1 + if 0 <= pos < len(self._components): + return self._components[pos] msg = f'{cls.__name__!r} object has no attribute {name!r}' raise AttributeError(msg) diff --git a/16-op-overloading/vector_v8.py b/16-op-overloading/vector_v8.py index ee2cb48..44c05fd 100644 --- a/16-op-overloading/vector_v8.py +++ b/16-op-overloading/vector_v8.py @@ -361,14 +361,16 @@ def __getitem__(self, key): index = operator.index(key) return self._components[index] - shortcut_names = 'xyzt' + __match_args__ = ('x', 'y', 'z', 't') def __getattr__(self, name): cls = type(self) - if len(name) == 1: - pos = cls.shortcut_names.find(name) - if 0 <= pos < len(self._components): - return self._components[pos] + try: + pos = cls.__match_args__.index(name) + except ValueError: + pos = -1 + if 0 <= pos < len(self._components): + return self._components[pos] msg = f'{cls.__name__!r} object has no attribute {name!r}' raise AttributeError(msg) From fa1cd6bd5ef63e3623e71debe3f218a6befdbc64 Mon Sep 17 00:00:00 2001 From: kbaikov Date: Sun, 22 Aug 2021 16:29:22 +0200 Subject: [PATCH 064/127] Use fixture instead of yield_fixture The old @pytest.yield_fixture causes a deprecation warning: Use @pytest.fixture instead; they are the same. def records(): --- 23-dyn-attr-prop/oscon/test_schedule_v1.py | 2 +- 23-dyn-attr-prop/oscon/test_schedule_v2.py | 2 +- 23-dyn-attr-prop/oscon/test_schedule_v3.py | 2 +- 23-dyn-attr-prop/oscon/test_schedule_v4.py | 2 +- 23-dyn-attr-prop/oscon/test_schedule_v5.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/23-dyn-attr-prop/oscon/test_schedule_v1.py b/23-dyn-attr-prop/oscon/test_schedule_v1.py index 643f04f..cfd7dd0 100644 --- a/23-dyn-attr-prop/oscon/test_schedule_v1.py +++ b/23-dyn-attr-prop/oscon/test_schedule_v1.py @@ -3,7 +3,7 @@ import schedule_v1 as schedule -@pytest.yield_fixture +@pytest.fixture def records(): yield schedule.load(schedule.JSON_PATH) diff --git a/23-dyn-attr-prop/oscon/test_schedule_v2.py b/23-dyn-attr-prop/oscon/test_schedule_v2.py index a11b24d..e15d640 100644 --- a/23-dyn-attr-prop/oscon/test_schedule_v2.py +++ b/23-dyn-attr-prop/oscon/test_schedule_v2.py @@ -2,7 +2,7 @@ import schedule_v2 as schedule -@pytest.yield_fixture +@pytest.fixture def records(): yield schedule.load(schedule.JSON_PATH) diff --git a/23-dyn-attr-prop/oscon/test_schedule_v3.py b/23-dyn-attr-prop/oscon/test_schedule_v3.py index 1b7ea75..d9436d4 100644 --- a/23-dyn-attr-prop/oscon/test_schedule_v3.py +++ b/23-dyn-attr-prop/oscon/test_schedule_v3.py @@ -2,7 +2,7 @@ import schedule_v3 as schedule -@pytest.yield_fixture +@pytest.fixture def records(): yield schedule.load(schedule.JSON_PATH) diff --git a/23-dyn-attr-prop/oscon/test_schedule_v4.py b/23-dyn-attr-prop/oscon/test_schedule_v4.py index 1e5560a..4980bbd 100644 --- a/23-dyn-attr-prop/oscon/test_schedule_v4.py +++ b/23-dyn-attr-prop/oscon/test_schedule_v4.py @@ -2,7 +2,7 @@ import schedule_v4 as schedule -@pytest.yield_fixture +@pytest.fixture def records(): yield schedule.load(schedule.JSON_PATH) diff --git a/23-dyn-attr-prop/oscon/test_schedule_v5.py b/23-dyn-attr-prop/oscon/test_schedule_v5.py index 2151844..e3a5206 100644 --- a/23-dyn-attr-prop/oscon/test_schedule_v5.py +++ b/23-dyn-attr-prop/oscon/test_schedule_v5.py @@ -2,7 +2,7 @@ import schedule_v5 as schedule -@pytest.yield_fixture +@pytest.fixture def records(): yield schedule.load(schedule.JSON_PATH) From dd535abcf76739d451c2baaf7813b8390a7dfe88 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 25 Aug 2021 14:46:57 -0300 Subject: [PATCH 065/127] sync from Atlas --- 08-def-type-hints/columnize.py | 15 +++++++------ 15-more-types/cast/find.py | 2 +- 15-more-types/protocol/abs_demo.py | 4 ---- 15-more-types/protocol/mymax/mymax.py | 3 ++- 15-more-types/typeddict/books.py | 6 +++--- 15-more-types/typeddict/books_any.py | 4 ++-- 16-op-overloading/bingoaddable.py | 8 +++---- 17-it-generator/aritprog_v1.py | 2 +- 17-it-generator/aritprog_v2.py | 2 +- 17-it-generator/columnize_iter.py | 21 +++++++++--------- 17-it-generator/iter_gen_type.py | 13 +++++++++++ 17-it-generator/tree/step2/tree.py | 2 +- 23-dyn-attr-prop/oscon/data/osconfeed.json | 2 +- 23-dyn-attr-prop/oscon/demo_schedule2.py | 25 ---------------------- 23-dyn-attr-prop/oscon/test_schedule_v3.py | 2 +- 23-dyn-attr-prop/pseudo_construction.py | 2 +- 16 files changed, 51 insertions(+), 62 deletions(-) create mode 100644 17-it-generator/iter_gen_type.py delete mode 100755 23-dyn-attr-prop/oscon/demo_schedule2.py diff --git a/08-def-type-hints/columnize.py b/08-def-type-hints/columnize.py index 66b72d8..8f4e92b 100644 --- a/08-def-type-hints/columnize.py +++ b/08-def-type-hints/columnize.py @@ -1,9 +1,11 @@ # tag::COLUMNIZE[] from collections.abc import Sequence -def columnize(sequence: Sequence[str], num_columns: int = 0) -> list[tuple[str, ...]]: +def columnize( + sequence: Sequence[str], num_columns: int = 0 +) -> list[tuple[str, ...]]: if num_columns == 0: - num_columns = round(len(sequence) ** .5) + num_columns = round(len(sequence) ** 0.5) num_rows, reminder = divmod(len(sequence), num_columns) num_rows += bool(reminder) return [tuple(sequence[i::num_rows]) for i in range(num_rows)] @@ -11,10 +13,11 @@ def columnize(sequence: Sequence[str], num_columns: int = 0) -> list[tuple[str, def demo() -> None: - nato = ('Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India' - ' Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo' - ' Sierra Tango Uniform Victor Whiskey X-ray Yankee Zulu' - ).split() + nato = ( + 'Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India' + ' Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo' + ' Sierra Tango Uniform Victor Whiskey X-ray Yankee Zulu' + ).split() for row in columnize(nato, 4): for word in row: diff --git a/15-more-types/cast/find.py b/15-more-types/cast/find.py index c853ea8..e79fe5b 100644 --- a/15-more-types/cast/find.py +++ b/15-more-types/cast/find.py @@ -3,7 +3,7 @@ def find_first_str(a: list[object]) -> str: index = next(i for i, x in enumerate(a) if isinstance(x, str)) - # We only get here if there's at least one string in a + # We only get here if there's at least one string return cast(str, a[index]) # end::CAST[] diff --git a/15-more-types/protocol/abs_demo.py b/15-more-types/protocol/abs_demo.py index 6af78ea..9c0b74c 100755 --- a/15-more-types/protocol/abs_demo.py +++ b/15-more-types/protocol/abs_demo.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python3 - -# tag::ABS_DEMO[] import math from typing import NamedTuple, SupportsAbs @@ -31,4 +28,3 @@ def is_unit(v: SupportsAbs[float]) -> bool: # <2> assert is_unit(v4) print('OK') -# end::ABS_DEMO[] diff --git a/15-more-types/protocol/mymax/mymax.py b/15-more-types/protocol/mymax/mymax.py index 5b9f4b2..6096985 100644 --- a/15-more-types/protocol/mymax/mymax.py +++ b/15-more-types/protocol/mymax/mymax.py @@ -1,5 +1,6 @@ # tag::MYMAX_TYPES[] -from typing import Protocol, Any, TypeVar, overload, Callable, Iterable, Union +from collections.abc import Callable, Iterable +from typing import Protocol, Any, TypeVar, overload, Union class SupportsLessThan(Protocol): def __lt__(self, other: Any) -> bool: ... diff --git a/15-more-types/typeddict/books.py b/15-more-types/typeddict/books.py index e33e21e..8caca46 100644 --- a/15-more-types/typeddict/books.py +++ b/15-more-types/typeddict/books.py @@ -1,6 +1,6 @@ +import json # tag::BOOKDICT[] from typing import TypedDict -import json class BookDict(TypedDict): isbn: str @@ -10,14 +10,14 @@ class BookDict(TypedDict): # end::BOOKDICT[] # tag::TOXML[] -AUTHOR_EL = '{}' +AUTHOR_ELEMENT = '{}' def to_xml(book: BookDict) -> str: # <1> elements: list[str] = [] # <2> for key, value in book.items(): if isinstance(value, list): # <3> elements.extend( - AUTHOR_EL.format(n) for n in value) # <4> + AUTHOR_ELEMENT.format(n) for n in value) # <4> else: tag = key.upper() elements.append(f'<{tag}>{value}') diff --git a/15-more-types/typeddict/books_any.py b/15-more-types/typeddict/books_any.py index 49a544e..0a5e1e4 100644 --- a/15-more-types/typeddict/books_any.py +++ b/15-more-types/typeddict/books_any.py @@ -10,13 +10,13 @@ class BookDict(TypedDict): # end::BOOKDICT[] # tag::TOXML[] -AUTHOR_EL = '{}' +AUTHOR_ELEMENT = '{}' def to_xml(book: BookDict) -> str: # <1> elements: List[str] = [] # <2> for key, value in book.items(): if isinstance(value, list): # <3> - elements.extend(AUTHOR_EL.format(n) + elements.extend(AUTHOR_ELEMENT.format(n) for n in value) else: tag = key.upper() diff --git a/16-op-overloading/bingoaddable.py b/16-op-overloading/bingoaddable.py index 38a70c7..da667d4 100644 --- a/16-op-overloading/bingoaddable.py +++ b/16-op-overloading/bingoaddable.py @@ -46,7 +46,7 @@ >>> globe += 1 # <6> Traceback (most recent call last): ... - TypeError: right operand in += must be 'AddableBingoCage' or an iterable + TypeError: right operand in += must be 'Tombola' or an iterable # end::ADDABLE_BINGO_IADD_DEMO[] @@ -72,9 +72,9 @@ def __iadd__(self, other): try: other_iterable = iter(other) # <4> except TypeError: # <5> - self_cls = type(self).__name__ - msg = "right operand in += must be {!r} or an iterable" - raise TypeError(msg.format(self_cls)) + msg = ('right operand in += must be ' + "'Tombola' or an iterable") + raise TypeError(msg) self.load(other_iterable) # <6> return self # <7> diff --git a/17-it-generator/aritprog_v1.py b/17-it-generator/aritprog_v1.py index 2ae66c4..69259bc 100644 --- a/17-it-generator/aritprog_v1.py +++ b/17-it-generator/aritprog_v1.py @@ -19,7 +19,7 @@ >>> from decimal import Decimal >>> ap = ArithmeticProgression(0, Decimal('.1'), .3) >>> list(ap) - [Decimal('0.0'), Decimal('0.1'), Decimal('0.2')] + [Decimal('0'), Decimal('0.1'), Decimal('0.2')] # end::ARITPROG_CLASS_DEMO[] """ diff --git a/17-it-generator/aritprog_v2.py b/17-it-generator/aritprog_v2.py index be211d8..42e27f8 100644 --- a/17-it-generator/aritprog_v2.py +++ b/17-it-generator/aritprog_v2.py @@ -14,7 +14,7 @@ >>> from decimal import Decimal >>> ap = aritprog_gen(0, Decimal('.1'), .3) >>> list(ap) - [Decimal('0.0'), Decimal('0.1'), Decimal('0.2')] + [Decimal('0'), Decimal('0.1'), Decimal('0.2')] """ diff --git a/17-it-generator/columnize_iter.py b/17-it-generator/columnize_iter.py index c9a6f7d..cee0f13 100644 --- a/17-it-generator/columnize_iter.py +++ b/17-it-generator/columnize_iter.py @@ -1,26 +1,27 @@ # tag::COLUMNIZE[] -from typing import Sequence, Tuple, Iterator +from collections.abc import Sequence, Iterator -def columnize(sequence: Sequence[str], num_columns: int = 0) -> Iterator[Tuple[str, ...]]: +def columnize( + sequence: Sequence[str], num_columns: int = 0 +) -> Iterator[tuple[str, ...]]: # <1> if num_columns == 0: - num_columns = round(len(sequence) ** .5) + num_columns = round(len(sequence) ** 0.5) num_rows, reminder = divmod(len(sequence), num_columns) num_rows += bool(reminder) - return (tuple(sequence[i::num_rows]) for i in range(num_rows)) + return (tuple(sequence[i::num_rows]) for i in range(num_rows)) # <2> # end::COLUMNIZE[] - def demo() -> None: - nato = ('Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India' - ' Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo' - ' Sierra Tango Uniform Victor Whiskey X-ray Yankee Zulu' - ).split() + nato = ( + 'Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India' + ' Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo' + ' Sierra Tango Uniform Victor Whiskey X-ray Yankee Zulu' + ).split() for row in columnize(nato, 4): for word in row: print(f'{word:15}', end='') print() - if __name__ == '__main__': demo() diff --git a/17-it-generator/iter_gen_type.py b/17-it-generator/iter_gen_type.py new file mode 100644 index 0000000..1730d87 --- /dev/null +++ b/17-it-generator/iter_gen_type.py @@ -0,0 +1,13 @@ +from collections.abc import Iterator +from keyword import kwlist +from typing import TYPE_CHECKING + +short_kw = (k for k in kwlist if len(k) < 5) # <1> + +if TYPE_CHECKING: + reveal_type(short_kw) # <2> + +long_kw: Iterator[str] = (k for k in kwlist if len(k) >= 4) # <3> + +if TYPE_CHECKING: # <4> + reveal_type(long_kw) diff --git a/17-it-generator/tree/step2/tree.py b/17-it-generator/tree/step2/tree.py index d4078b4..6a0e1fa 100644 --- a/17-it-generator/tree/step2/tree.py +++ b/17-it-generator/tree/step2/tree.py @@ -9,7 +9,7 @@ def sub_tree(cls): def display(cls): - for cls_name, level in tree(cls): + for cls_name, level in tree(cls): # <3> indent = ' ' * 4 * level print(f'{indent}{cls_name}') diff --git a/23-dyn-attr-prop/oscon/data/osconfeed.json b/23-dyn-attr-prop/oscon/data/osconfeed.json index 50ed313..7bea08d 100644 --- a/23-dyn-attr-prop/oscon/data/osconfeed.json +++ b/23-dyn-attr-prop/oscon/data/osconfeed.json @@ -1243,7 +1243,7 @@ "time_start": "2014-07-21 13:30:00", "time_stop": "2014-07-21 17:00:00", "venue_serial": 1451, - "description": "Metaprograming in Python is fun and profitable thanks to its rich Data Model \u2013 APIs that let you handle functions, modules and even classes as objects that you can create, inspect and modify at runtime. The Data Model also enables your own objects to support infix operators, become iterable and emulate collections. This workshop shows how, through a diverse selection of examples and exercises.", + "description": "Metaprogramming in Python is fun and profitable thanks to its rich Data Model \u2013 APIs that let you handle functions, modules and even classes as objects that you can create, inspect and modify at runtime. The Data Model also enables your own objects to support infix operators, become iterable and emulate collections. This workshop shows how, through a diverse selection of examples and exercises.", "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34107", "speakers": [150170], "categories": [ diff --git a/23-dyn-attr-prop/oscon/demo_schedule2.py b/23-dyn-attr-prop/oscon/demo_schedule2.py deleted file mode 100755 index 12fd440..0000000 --- a/23-dyn-attr-prop/oscon/demo_schedule2.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python3 - -import shelve - -from schedule_v2 import DB_NAME, CONFERENCE, load_db -from schedule_v2 import DbRecord, Event - -with shelve.open(DB_NAME) as db: - if CONFERENCE not in db: - load_db(db) - - DbRecord.set_db(db) - event = DbRecord.fetch('event.33950') - print(event) - print(event.venue) - print(event.venue.name) - for spkr in event.speakers: - print(f'{spkr.serial}:', spkr.name) - - print(repr(Event.venue)) - - event2 = DbRecord.fetch('event.33451') - print(event2) - print(event2.fetch) - print(event2.venue) \ No newline at end of file diff --git a/23-dyn-attr-prop/oscon/test_schedule_v3.py b/23-dyn-attr-prop/oscon/test_schedule_v3.py index d9436d4..12332c4 100644 --- a/23-dyn-attr-prop/oscon/test_schedule_v3.py +++ b/23-dyn-attr-prop/oscon/test_schedule_v3.py @@ -56,4 +56,4 @@ def test_event_speakers(): def test_event_no_speakers(): event = schedule.Record.fetch('event.36848') - assert event.speakers == [] \ No newline at end of file + assert event.speakers == [] diff --git a/23-dyn-attr-prop/pseudo_construction.py b/23-dyn-attr-prop/pseudo_construction.py index 5656192..af651a2 100644 --- a/23-dyn-attr-prop/pseudo_construction.py +++ b/23-dyn-attr-prop/pseudo_construction.py @@ -1,4 +1,4 @@ -# pseudo-code for object construction +# pseudocode for object construction def make(the_class, some_arg): new_object = the_class.__new__(some_arg) if isinstance(new_object, the_class): From 4ae4096c4cb8848d0c1dcf24ff2cb7efb72e3b2b Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Fri, 10 Sep 2021 12:34:39 -0300 Subject: [PATCH 066/127] renumbering chapters >= 19 --- .../primes/log-procs.txt | 0 .../primes/primes.py | 0 .../primes/procs.py | 0 .../primes/py36/primes.py | 0 .../primes/py36/procs.py | 0 .../primes/run_procs.sh | 0 .../primes/sequential.py | 0 .../primes/spinner_prime_async_broken.py | 0 .../primes/spinner_prime_async_nap.py | 0 .../primes/spinner_prime_proc.py | 0 .../primes/spinner_prime_thread.py | 0 .../primes/stats-procs.ipynb | 0 .../primes/threads.py | 0 .../spinner_async.py | 0 .../spinner_async_experiment.py | 0 .../spinner_proc.py | 0 .../spinner_thread.py | 0 19-coroutine/README.rst | 4 - 19-coroutine/coro_exc_demo.py | 66 ----- 19-coroutine/coro_finally_demo.py | 61 ----- 19-coroutine/coroaverager0.py | 28 -- 19-coroutine/coroaverager1.py | 30 --- 19-coroutine/coroaverager2.py | 61 ----- 19-coroutine/coroaverager3.py | 107 -------- 19-coroutine/coroutil.py | 12 - 19-coroutine/taxi_sim.py | 203 -------------- 19-coroutine/taxi_sim0.py | 255 ------------------ 19-coroutine/taxi_sim_delay.py | 215 --------------- 19-coroutine/yield_from_expansion.py | 52 ---- .../yield_from_expansion_simplified.py | 32 --- .../demo_executor_map.py | 0 .../getflags/.gitignore | 0 .../getflags/country_codes.txt | 0 .../getflags/downloaded/.gitignore | 0 {21-futures => 20-futures}/getflags/flags.py | 0 {21-futures => 20-futures}/getflags/flags.zip | Bin .../getflags/flags2_asyncio.py | 0 .../getflags/flags2_asyncio_executor.py | 0 .../getflags/flags2_common.py | 0 .../getflags/flags2_sequential.py | 0 .../getflags/flags2_threadpool.py | 0 .../getflags/flags3_asyncio.py | 0 .../getflags/flags_asyncio.py | 0 .../getflags/flags_threadpool.py | 0 .../getflags/flags_threadpool_futures.py | 0 .../getflags/requirements.txt | 0 .../getflags/slow_server.py | 0 {21-futures => 20-futures}/primes/primes.py | 0 .../primes/proc_pool.py | 0 {22-async => 21-async}/README.rst | 0 {22-async => 21-async}/domains/README.rst | 0 .../domains/asyncio/blogdom.py | 0 .../domains/asyncio/domaincheck.py | 0 .../domains/asyncio/domainlib.py | 0 .../domains/curio/blogdom.py | 0 .../domains/curio/domaincheck.py | 0 .../domains/curio/domainlib.py | 0 .../domains/curio/requirements.txt | 0 {22-async => 21-async}/mojifinder/README.md | 0 {22-async => 21-async}/mojifinder/bottle.py | 0 .../mojifinder/charindex.py | 0 .../mojifinder/requirements.txt | 0 .../mojifinder/static/form.html | 0 .../mojifinder/tcp_mojifinder.py | 0 .../mojifinder/web_mojifinder.py | 0 .../mojifinder/web_mojifinder_bottle.py | 0 .../README.rst | 0 .../blackknight.py | 0 .../bulkfood/bulkfood_v1.py | 0 .../bulkfood/bulkfood_v2.py | 0 .../bulkfood/bulkfood_v2b.py | 0 .../bulkfood/bulkfood_v2prop.py | 0 .../doc_property.py | 0 .../oscon/data/osconfeed.json | 0 .../oscon/demo_schedule2.py | 0 .../oscon/explore0.py | 0 .../oscon/explore1.py | 0 .../oscon/explore2.py | 0 .../oscon/osconfeed-sample.json | 0 .../oscon/osconfeed_explore.rst | 0 .../oscon/runtests.sh | 0 .../oscon/schedule_v1.py | 0 .../oscon/schedule_v2.py | 0 .../oscon/schedule_v3.py | 0 .../oscon/schedule_v4.py | 0 .../oscon/schedule_v4_hasattr.py | 0 .../oscon/schedule_v5.py | 0 .../oscon/test_schedule_v1.py | 0 .../oscon/test_schedule_v2.py | 0 .../oscon/test_schedule_v3.py | 0 .../oscon/test_schedule_v4.py | 0 .../oscon/test_schedule_v5.py | 0 .../pseudo_construction.py | 0 {24-descriptor => 23-descriptor}/README.rst | 0 .../bulkfood/bulkfood_v3.py | 0 .../bulkfood/bulkfood_v4.py | 0 .../bulkfood/bulkfood_v4c.py | 0 .../bulkfood/bulkfood_v5.py | 0 .../bulkfood/model_v4c.py | 0 .../bulkfood/model_v5.py | 0 .../descriptorkinds.py | 0 .../descriptorkinds_dump.py | 0 .../method_is_descriptor.py | 0 .../autoconst/autoconst.py | 0 .../autoconst/autoconst_demo.py | 0 .../bulkfood/README.md | 0 .../bulkfood/bulkfood_v6.py | 0 .../bulkfood/bulkfood_v7.py | 0 .../bulkfood/bulkfood_v8.py | 0 .../bulkfood/model_v6.py | 0 .../bulkfood/model_v7.py | 0 .../bulkfood/model_v8.py | 0 .../checked/decorator/checkeddeco.py | 0 .../checked/decorator/checkeddeco_demo.py | 0 .../checked/decorator/checkeddeco_test.py | 0 .../checked/initsub/checked_demo.py | 0 .../checked/initsub/checkedlib.py | 0 .../checked/initsub/checkedlib_test.py | 0 .../checked/metaclass/checked_demo.py | 0 .../checked/metaclass/checkedlib.py | 0 .../checked/metaclass/checkedlib_test.py | 0 .../evaltime/builderlib.py | 0 .../evaltime/evaldemo.py | 0 .../evaltime/evaldemo_meta.py | 0 .../evaltime/metalib.py | 0 .../factories.py | 0 .../factories_ducktyped.py | 0 .../hours/hours.py | 0 .../hours/hours_test.py | 0 .../metabunch/README.md | 0 .../metabunch/from3.6/bunch.py | 0 .../metabunch/from3.6/bunch_test.py | 0 .../metabunch/nutshell3e/bunch.py | 0 .../metabunch/nutshell3e/bunch_test.py | 0 .../metabunch/original/bunch.py | 0 .../metabunch/original/bunch_test.py | 0 .../metabunch/pre3.6/bunch.py | 0 .../metabunch/pre3.6/bunch_test.py | 0 .../persistent/.gitignore | 0 .../persistent/dblib.py | 0 .../persistent/dblib_test.py | 0 .../persistent/persistlib.py | 0 .../persistent/persistlib_test.py | 0 .../qualname/fakedjango.py | 0 .../qualname/models.py | 0 .../sentinel/sentinel.py | 0 .../sentinel/sentinel_test.py | 0 .../setattr/example_from_leo.py | 0 .../slots/slots_timing.py | 0 .../tinyenums/microenum.py | 0 .../tinyenums/microenum_demo.py | 0 .../tinyenums/nanoenum.py | 0 .../tinyenums/nanoenum_demo.py | 0 README.md | 15 +- 154 files changed, 7 insertions(+), 1134 deletions(-) rename {20-concurrency => 19-concurrency}/primes/log-procs.txt (100%) rename {20-concurrency => 19-concurrency}/primes/primes.py (100%) rename {20-concurrency => 19-concurrency}/primes/procs.py (100%) rename {20-concurrency => 19-concurrency}/primes/py36/primes.py (100%) rename {20-concurrency => 19-concurrency}/primes/py36/procs.py (100%) rename {20-concurrency => 19-concurrency}/primes/run_procs.sh (100%) rename {20-concurrency => 19-concurrency}/primes/sequential.py (100%) rename {20-concurrency => 19-concurrency}/primes/spinner_prime_async_broken.py (100%) rename {20-concurrency => 19-concurrency}/primes/spinner_prime_async_nap.py (100%) rename {20-concurrency => 19-concurrency}/primes/spinner_prime_proc.py (100%) rename {20-concurrency => 19-concurrency}/primes/spinner_prime_thread.py (100%) rename {20-concurrency => 19-concurrency}/primes/stats-procs.ipynb (100%) rename {20-concurrency => 19-concurrency}/primes/threads.py (100%) rename {20-concurrency => 19-concurrency}/spinner_async.py (100%) rename {20-concurrency => 19-concurrency}/spinner_async_experiment.py (100%) rename {20-concurrency => 19-concurrency}/spinner_proc.py (100%) rename {20-concurrency => 19-concurrency}/spinner_thread.py (100%) delete mode 100644 19-coroutine/README.rst delete mode 100644 19-coroutine/coro_exc_demo.py delete mode 100644 19-coroutine/coro_finally_demo.py delete mode 100644 19-coroutine/coroaverager0.py delete mode 100644 19-coroutine/coroaverager1.py delete mode 100644 19-coroutine/coroaverager2.py delete mode 100644 19-coroutine/coroaverager3.py delete mode 100644 19-coroutine/coroutil.py delete mode 100644 19-coroutine/taxi_sim.py delete mode 100644 19-coroutine/taxi_sim0.py delete mode 100644 19-coroutine/taxi_sim_delay.py delete mode 100644 19-coroutine/yield_from_expansion.py delete mode 100644 19-coroutine/yield_from_expansion_simplified.py rename {21-futures => 20-futures}/demo_executor_map.py (100%) rename {21-futures => 20-futures}/getflags/.gitignore (100%) rename {21-futures => 20-futures}/getflags/country_codes.txt (100%) rename {21-futures => 20-futures}/getflags/downloaded/.gitignore (100%) rename {21-futures => 20-futures}/getflags/flags.py (100%) rename {21-futures => 20-futures}/getflags/flags.zip (100%) rename {21-futures => 20-futures}/getflags/flags2_asyncio.py (100%) rename {21-futures => 20-futures}/getflags/flags2_asyncio_executor.py (100%) rename {21-futures => 20-futures}/getflags/flags2_common.py (100%) rename {21-futures => 20-futures}/getflags/flags2_sequential.py (100%) rename {21-futures => 20-futures}/getflags/flags2_threadpool.py (100%) rename {21-futures => 20-futures}/getflags/flags3_asyncio.py (100%) rename {21-futures => 20-futures}/getflags/flags_asyncio.py (100%) rename {21-futures => 20-futures}/getflags/flags_threadpool.py (100%) rename {21-futures => 20-futures}/getflags/flags_threadpool_futures.py (100%) rename {21-futures => 20-futures}/getflags/requirements.txt (100%) rename {21-futures => 20-futures}/getflags/slow_server.py (100%) rename {21-futures => 20-futures}/primes/primes.py (100%) rename {21-futures => 20-futures}/primes/proc_pool.py (100%) rename {22-async => 21-async}/README.rst (100%) rename {22-async => 21-async}/domains/README.rst (100%) rename {22-async => 21-async}/domains/asyncio/blogdom.py (100%) rename {22-async => 21-async}/domains/asyncio/domaincheck.py (100%) rename {22-async => 21-async}/domains/asyncio/domainlib.py (100%) rename {22-async => 21-async}/domains/curio/blogdom.py (100%) rename {22-async => 21-async}/domains/curio/domaincheck.py (100%) rename {22-async => 21-async}/domains/curio/domainlib.py (100%) rename {22-async => 21-async}/domains/curio/requirements.txt (100%) rename {22-async => 21-async}/mojifinder/README.md (100%) rename {22-async => 21-async}/mojifinder/bottle.py (100%) rename {22-async => 21-async}/mojifinder/charindex.py (100%) rename {22-async => 21-async}/mojifinder/requirements.txt (100%) rename {22-async => 21-async}/mojifinder/static/form.html (100%) rename {22-async => 21-async}/mojifinder/tcp_mojifinder.py (100%) rename {22-async => 21-async}/mojifinder/web_mojifinder.py (100%) rename {22-async => 21-async}/mojifinder/web_mojifinder_bottle.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/README.rst (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/blackknight.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/bulkfood/bulkfood_v1.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/bulkfood/bulkfood_v2.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/bulkfood/bulkfood_v2b.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/bulkfood/bulkfood_v2prop.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/doc_property.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/data/osconfeed.json (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/demo_schedule2.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/explore0.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/explore1.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/explore2.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/osconfeed-sample.json (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/osconfeed_explore.rst (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/runtests.sh (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/schedule_v1.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/schedule_v2.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/schedule_v3.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/schedule_v4.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/schedule_v4_hasattr.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/schedule_v5.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/test_schedule_v1.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/test_schedule_v2.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/test_schedule_v3.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/test_schedule_v4.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/oscon/test_schedule_v5.py (100%) rename {23-dyn-attr-prop => 22-dyn-attr-prop}/pseudo_construction.py (100%) rename {24-descriptor => 23-descriptor}/README.rst (100%) rename {24-descriptor => 23-descriptor}/bulkfood/bulkfood_v3.py (100%) rename {24-descriptor => 23-descriptor}/bulkfood/bulkfood_v4.py (100%) rename {24-descriptor => 23-descriptor}/bulkfood/bulkfood_v4c.py (100%) rename {24-descriptor => 23-descriptor}/bulkfood/bulkfood_v5.py (100%) rename {24-descriptor => 23-descriptor}/bulkfood/model_v4c.py (100%) rename {24-descriptor => 23-descriptor}/bulkfood/model_v5.py (100%) rename {24-descriptor => 23-descriptor}/descriptorkinds.py (100%) rename {24-descriptor => 23-descriptor}/descriptorkinds_dump.py (100%) rename {24-descriptor => 23-descriptor}/method_is_descriptor.py (100%) rename {25-class-metaprog => 24-class-metaprog}/autoconst/autoconst.py (100%) rename {25-class-metaprog => 24-class-metaprog}/autoconst/autoconst_demo.py (100%) rename {25-class-metaprog => 24-class-metaprog}/bulkfood/README.md (100%) rename {25-class-metaprog => 24-class-metaprog}/bulkfood/bulkfood_v6.py (100%) rename {25-class-metaprog => 24-class-metaprog}/bulkfood/bulkfood_v7.py (100%) rename {25-class-metaprog => 24-class-metaprog}/bulkfood/bulkfood_v8.py (100%) rename {25-class-metaprog => 24-class-metaprog}/bulkfood/model_v6.py (100%) rename {25-class-metaprog => 24-class-metaprog}/bulkfood/model_v7.py (100%) rename {25-class-metaprog => 24-class-metaprog}/bulkfood/model_v8.py (100%) rename {25-class-metaprog => 24-class-metaprog}/checked/decorator/checkeddeco.py (100%) rename {25-class-metaprog => 24-class-metaprog}/checked/decorator/checkeddeco_demo.py (100%) rename {25-class-metaprog => 24-class-metaprog}/checked/decorator/checkeddeco_test.py (100%) rename {25-class-metaprog => 24-class-metaprog}/checked/initsub/checked_demo.py (100%) rename {25-class-metaprog => 24-class-metaprog}/checked/initsub/checkedlib.py (100%) rename {25-class-metaprog => 24-class-metaprog}/checked/initsub/checkedlib_test.py (100%) rename {25-class-metaprog => 24-class-metaprog}/checked/metaclass/checked_demo.py (100%) rename {25-class-metaprog => 24-class-metaprog}/checked/metaclass/checkedlib.py (100%) rename {25-class-metaprog => 24-class-metaprog}/checked/metaclass/checkedlib_test.py (100%) rename {25-class-metaprog => 24-class-metaprog}/evaltime/builderlib.py (100%) rename {25-class-metaprog => 24-class-metaprog}/evaltime/evaldemo.py (100%) rename {25-class-metaprog => 24-class-metaprog}/evaltime/evaldemo_meta.py (100%) rename {25-class-metaprog => 24-class-metaprog}/evaltime/metalib.py (100%) rename {25-class-metaprog => 24-class-metaprog}/factories.py (100%) rename {25-class-metaprog => 24-class-metaprog}/factories_ducktyped.py (100%) rename {25-class-metaprog => 24-class-metaprog}/hours/hours.py (100%) rename {25-class-metaprog => 24-class-metaprog}/hours/hours_test.py (100%) rename {25-class-metaprog => 24-class-metaprog}/metabunch/README.md (100%) rename {25-class-metaprog => 24-class-metaprog}/metabunch/from3.6/bunch.py (100%) rename {25-class-metaprog => 24-class-metaprog}/metabunch/from3.6/bunch_test.py (100%) rename {25-class-metaprog => 24-class-metaprog}/metabunch/nutshell3e/bunch.py (100%) rename {25-class-metaprog => 24-class-metaprog}/metabunch/nutshell3e/bunch_test.py (100%) rename {25-class-metaprog => 24-class-metaprog}/metabunch/original/bunch.py (100%) rename {25-class-metaprog => 24-class-metaprog}/metabunch/original/bunch_test.py (100%) rename {25-class-metaprog => 24-class-metaprog}/metabunch/pre3.6/bunch.py (100%) rename {25-class-metaprog => 24-class-metaprog}/metabunch/pre3.6/bunch_test.py (100%) rename {25-class-metaprog => 24-class-metaprog}/persistent/.gitignore (100%) rename {25-class-metaprog => 24-class-metaprog}/persistent/dblib.py (100%) rename {25-class-metaprog => 24-class-metaprog}/persistent/dblib_test.py (100%) rename {25-class-metaprog => 24-class-metaprog}/persistent/persistlib.py (100%) rename {25-class-metaprog => 24-class-metaprog}/persistent/persistlib_test.py (100%) rename {25-class-metaprog => 24-class-metaprog}/qualname/fakedjango.py (100%) rename {25-class-metaprog => 24-class-metaprog}/qualname/models.py (100%) rename {25-class-metaprog => 24-class-metaprog}/sentinel/sentinel.py (100%) rename {25-class-metaprog => 24-class-metaprog}/sentinel/sentinel_test.py (100%) rename {25-class-metaprog => 24-class-metaprog}/setattr/example_from_leo.py (100%) rename {25-class-metaprog => 24-class-metaprog}/slots/slots_timing.py (100%) rename {25-class-metaprog => 24-class-metaprog}/tinyenums/microenum.py (100%) rename {25-class-metaprog => 24-class-metaprog}/tinyenums/microenum_demo.py (100%) rename {25-class-metaprog => 24-class-metaprog}/tinyenums/nanoenum.py (100%) rename {25-class-metaprog => 24-class-metaprog}/tinyenums/nanoenum_demo.py (100%) diff --git a/20-concurrency/primes/log-procs.txt b/19-concurrency/primes/log-procs.txt similarity index 100% rename from 20-concurrency/primes/log-procs.txt rename to 19-concurrency/primes/log-procs.txt diff --git a/20-concurrency/primes/primes.py b/19-concurrency/primes/primes.py similarity index 100% rename from 20-concurrency/primes/primes.py rename to 19-concurrency/primes/primes.py diff --git a/20-concurrency/primes/procs.py b/19-concurrency/primes/procs.py similarity index 100% rename from 20-concurrency/primes/procs.py rename to 19-concurrency/primes/procs.py diff --git a/20-concurrency/primes/py36/primes.py b/19-concurrency/primes/py36/primes.py similarity index 100% rename from 20-concurrency/primes/py36/primes.py rename to 19-concurrency/primes/py36/primes.py diff --git a/20-concurrency/primes/py36/procs.py b/19-concurrency/primes/py36/procs.py similarity index 100% rename from 20-concurrency/primes/py36/procs.py rename to 19-concurrency/primes/py36/procs.py diff --git a/20-concurrency/primes/run_procs.sh b/19-concurrency/primes/run_procs.sh similarity index 100% rename from 20-concurrency/primes/run_procs.sh rename to 19-concurrency/primes/run_procs.sh diff --git a/20-concurrency/primes/sequential.py b/19-concurrency/primes/sequential.py similarity index 100% rename from 20-concurrency/primes/sequential.py rename to 19-concurrency/primes/sequential.py diff --git a/20-concurrency/primes/spinner_prime_async_broken.py b/19-concurrency/primes/spinner_prime_async_broken.py similarity index 100% rename from 20-concurrency/primes/spinner_prime_async_broken.py rename to 19-concurrency/primes/spinner_prime_async_broken.py diff --git a/20-concurrency/primes/spinner_prime_async_nap.py b/19-concurrency/primes/spinner_prime_async_nap.py similarity index 100% rename from 20-concurrency/primes/spinner_prime_async_nap.py rename to 19-concurrency/primes/spinner_prime_async_nap.py diff --git a/20-concurrency/primes/spinner_prime_proc.py b/19-concurrency/primes/spinner_prime_proc.py similarity index 100% rename from 20-concurrency/primes/spinner_prime_proc.py rename to 19-concurrency/primes/spinner_prime_proc.py diff --git a/20-concurrency/primes/spinner_prime_thread.py b/19-concurrency/primes/spinner_prime_thread.py similarity index 100% rename from 20-concurrency/primes/spinner_prime_thread.py rename to 19-concurrency/primes/spinner_prime_thread.py diff --git a/20-concurrency/primes/stats-procs.ipynb b/19-concurrency/primes/stats-procs.ipynb similarity index 100% rename from 20-concurrency/primes/stats-procs.ipynb rename to 19-concurrency/primes/stats-procs.ipynb diff --git a/20-concurrency/primes/threads.py b/19-concurrency/primes/threads.py similarity index 100% rename from 20-concurrency/primes/threads.py rename to 19-concurrency/primes/threads.py diff --git a/20-concurrency/spinner_async.py b/19-concurrency/spinner_async.py similarity index 100% rename from 20-concurrency/spinner_async.py rename to 19-concurrency/spinner_async.py diff --git a/20-concurrency/spinner_async_experiment.py b/19-concurrency/spinner_async_experiment.py similarity index 100% rename from 20-concurrency/spinner_async_experiment.py rename to 19-concurrency/spinner_async_experiment.py diff --git a/20-concurrency/spinner_proc.py b/19-concurrency/spinner_proc.py similarity index 100% rename from 20-concurrency/spinner_proc.py rename to 19-concurrency/spinner_proc.py diff --git a/20-concurrency/spinner_thread.py b/19-concurrency/spinner_thread.py similarity index 100% rename from 20-concurrency/spinner_thread.py rename to 19-concurrency/spinner_thread.py diff --git a/19-coroutine/README.rst b/19-coroutine/README.rst deleted file mode 100644 index 2771530..0000000 --- a/19-coroutine/README.rst +++ /dev/null @@ -1,4 +0,0 @@ -Sample code for Chapter 16 - "Coroutines" - -From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015) -http://shop.oreilly.com/product/0636920032519.do diff --git a/19-coroutine/coro_exc_demo.py b/19-coroutine/coro_exc_demo.py deleted file mode 100644 index a68684f..0000000 --- a/19-coroutine/coro_exc_demo.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -Coroutine closing demonstration:: - -# tag::DEMO_CORO_EXC_1[] - >>> exc_coro = demo_exc_handling() - >>> next(exc_coro) - -> coroutine started - >>> exc_coro.send(11) - -> coroutine received: 11 - >>> exc_coro.send(22) - -> coroutine received: 22 - >>> exc_coro.close() - >>> from inspect import getgeneratorstate - >>> getgeneratorstate(exc_coro) - 'GEN_CLOSED' - -# end::DEMO_CORO_EXC_1[] - -Coroutine handling exception:: - -# tag::DEMO_CORO_EXC_2[] - >>> exc_coro = demo_exc_handling() - >>> next(exc_coro) - -> coroutine started - >>> exc_coro.send(11) - -> coroutine received: 11 - >>> exc_coro.throw(DemoException) - *** DemoException handled. Continuing... - >>> getgeneratorstate(exc_coro) - 'GEN_SUSPENDED' - -# end::DEMO_CORO_EXC_2[] - -Coroutine not handling exception:: - -# tag::DEMO_CORO_EXC_3[] - >>> exc_coro = demo_exc_handling() - >>> next(exc_coro) - -> coroutine started - >>> exc_coro.send(11) - -> coroutine received: 11 - >>> exc_coro.throw(ZeroDivisionError) - Traceback (most recent call last): - ... - ZeroDivisionError - >>> getgeneratorstate(exc_coro) - 'GEN_CLOSED' - -# end::DEMO_CORO_EXC_3[] -""" - -# tag::EX_CORO_EXC[] -class DemoException(Exception): - """An exception type for the demonstration.""" - -def demo_exc_handling(): - print('-> coroutine started') - while True: - try: - x = yield - except DemoException: # <1> - print('*** DemoException handled. Continuing...') - else: # <2> - print(f'-> coroutine received: {x!r}') - raise RuntimeError('This line should never run.') # <3> -# end::EX_CORO_EXC[] diff --git a/19-coroutine/coro_finally_demo.py b/19-coroutine/coro_finally_demo.py deleted file mode 100644 index fd4545d..0000000 --- a/19-coroutine/coro_finally_demo.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Second coroutine closing demonstration:: - - >>> fin_coro = demo_finally() - >>> next(fin_coro) - -> coroutine started - >>> fin_coro.send(11) - -> coroutine received: 11 - >>> fin_coro.send(22) - -> coroutine received: 22 - >>> fin_coro.close() - -> coroutine ending - - -Second coroutine not handling exception:: - - >>> fin_coro = demo_finally() - >>> next(fin_coro) - -> coroutine started - >>> fin_coro.send(11) - -> coroutine received: 11 - >>> fin_coro.throw(ZeroDivisionError) # doctest: +SKIP - -> coroutine ending - Traceback (most recent call last): - File "", line 1, in - File "coro_exception_demos.py", line 109, in demo_finally - print(f'-> coroutine received: {x!r}') - ZeroDivisionError - - -The last test above must be skipped because the output '-> coroutine ending' -is not detected by doctest, which raises a false error. However, if you -run this file as shown below, you'll see that output "leak" into standard -output:: - - - $ python3 -m doctest coro_exception_demo.py - -> coroutine ending - -""" - - -# tag::EX_CORO_FINALLY[] -class DemoException(Exception): - """An exception type for the demonstration.""" - - -def demo_finally(): - print('-> coroutine started') - try: - while True: - try: - x = yield - except DemoException: - print('*** DemoException handled. Continuing...') - else: - print(f'-> coroutine received: {x!r}') - finally: - print('-> coroutine ending') - -# end::EX_CORO_FINALLY[] diff --git a/19-coroutine/coroaverager0.py b/19-coroutine/coroaverager0.py deleted file mode 100644 index 10ccfdc..0000000 --- a/19-coroutine/coroaverager0.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -A coroutine to compute a running average - -# tag::CORO_AVERAGER_TEST[] - >>> coro_avg = averager() # <1> - >>> next(coro_avg) # <2> - >>> coro_avg.send(10) # <3> - 10.0 - >>> coro_avg.send(30) - 20.0 - >>> coro_avg.send(5) - 15.0 - -# end::CORO_AVERAGER_TEST[] - -""" - -# tag::CORO_AVERAGER[] -def averager(): - total = 0.0 - count = 0 - average = None - while True: # <1> - term = yield average # <2> - total += term - count += 1 - average = total/count -# end::CORO_AVERAGER[] diff --git a/19-coroutine/coroaverager1.py b/19-coroutine/coroaverager1.py deleted file mode 100644 index 3b3c5e9..0000000 --- a/19-coroutine/coroaverager1.py +++ /dev/null @@ -1,30 +0,0 @@ -# tag::DECORATED_AVERAGER[] -""" -A coroutine to compute a running average - - >>> coro_avg = averager() # <1> - >>> from inspect import getgeneratorstate - >>> getgeneratorstate(coro_avg) # <2> - 'GEN_SUSPENDED' - >>> coro_avg.send(10) # <3> - 10.0 - >>> coro_avg.send(30) - 20.0 - >>> coro_avg.send(5) - 15.0 - -""" - -from coroutil import coroutine # <4> - -@coroutine # <5> -def averager(): # <6> - total = 0.0 - count = 0 - average = None - while True: - term = yield average - total += term - count += 1 - average = total/count -# end::DECORATED_AVERAGER[] diff --git a/19-coroutine/coroaverager2.py b/19-coroutine/coroaverager2.py deleted file mode 100644 index 9d724c0..0000000 --- a/19-coroutine/coroaverager2.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -A coroutine to compute a running average. - -Testing ``averager`` by itself:: - -# tag::RETURNING_AVERAGER_DEMO1[] - - >>> coro_avg = averager() - >>> next(coro_avg) - >>> coro_avg.send(10) # <1> - >>> coro_avg.send(30) - >>> coro_avg.send(6.5) - >>> coro_avg.send(None) # <2> - Traceback (most recent call last): - ... - StopIteration: Result(count=3, average=15.5) - -# end::RETURNING_AVERAGER_DEMO1[] - -Catching `StopIteration` to extract the value returned by -the coroutine:: - -# tag::RETURNING_AVERAGER_DEMO2[] - - >>> coro_avg = averager() - >>> next(coro_avg) - >>> coro_avg.send(10) - >>> coro_avg.send(30) - >>> coro_avg.send(6.5) - >>> try: - ... coro_avg.send(None) - ... except StopIteration as exc: - ... result = exc.value - ... - >>> result - Result(count=3, average=15.5) - -# end::RETURNING_AVERAGER_DEMO2[] - - -""" - -# tag::RETURNING_AVERAGER[] -from collections import namedtuple - -Result = namedtuple('Result', 'count average') - - -def averager(): - total = 0.0 - count = 0 - average = None - while True: - term = yield - if term is None: - break # <1> - total += term - count += 1 - average = total/count - return Result(count, average) # <2> -# end::RETURNING_AVERAGER[] diff --git a/19-coroutine/coroaverager3.py b/19-coroutine/coroaverager3.py deleted file mode 100644 index 2b24df9..0000000 --- a/19-coroutine/coroaverager3.py +++ /dev/null @@ -1,107 +0,0 @@ -""" -A coroutine to compute a running average. - -Testing ``averager`` by itself:: - - >>> coro_avg = averager() - >>> next(coro_avg) - >>> coro_avg.send(10) - >>> coro_avg.send(30) - >>> coro_avg.send(6.5) - >>> coro_avg.send(None) - Traceback (most recent call last): - ... - StopIteration: Result(count=3, average=15.5) - - -Driving it with ``yield from``:: - - >>> def summarize(results): - ... while True: - ... result = yield from averager() - ... results.append(result) - ... - >>> results = [] - >>> summary = summarize(results) - >>> next(summary) - >>> for height in data['girls;m']: - ... summary.send(height) - ... - >>> summary.send(None) - >>> for height in data['boys;m']: - ... summary.send(height) - ... - >>> summary.send(None) - >>> results == [ - ... Result(count=10, average=1.4279999999999997), - ... Result(count=9, average=1.3888888888888888) - ... ] - True - -""" - -# tag::YIELD_FROM_AVERAGER[] -from collections import namedtuple - -Result = namedtuple('Result', 'count average') - - -# the subgenerator -def averager(): # <1> - total = 0.0 - count = 0 - average = None - while True: - term = yield # <2> - if term is None: # <3> - break - total += term - count += 1 - average = total/count - return Result(count, average) # <4> - - -# the delegating generator -def grouper(results, key): # <5> - while True: # <6> - results[key] = yield from averager() # <7> - - -# the client code, a.k.a. the caller -def main(data): # <8> - results = {} - for key, values in data.items(): - group = grouper(results, key) # <9> - next(group) # <10> - for value in values: - group.send(value) # <11> - group.send(None) # important! <12> - - # print(results) # uncomment to debug - report(results) - - -# output report -def report(results): - for key, result in sorted(results.items()): - group, unit = key.split(';') - print(f'{result.count:2} {group:5}', - f'averaging {result.average:.2f}{unit}') - - -data = { - 'girls;kg': - [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5], - 'girls;m': - [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43], - 'boys;kg': - [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3], - 'boys;m': - [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46], -} - - -if __name__ == '__main__': - main(data) - -# end::YIELD_FROM_AVERAGER[] diff --git a/19-coroutine/coroutil.py b/19-coroutine/coroutil.py deleted file mode 100644 index 4420db8..0000000 --- a/19-coroutine/coroutil.py +++ /dev/null @@ -1,12 +0,0 @@ -# tag::CORO_DECO[] -from functools import wraps - -def coroutine(func): - """Decorator: primes `func` by advancing to first `yield`""" - @wraps(func) - def primer(*args, **kwargs): # <1> - gen = func(*args, **kwargs) # <2> - next(gen) # <3> - return gen # <4> - return primer -# end::CORO_DECO[] diff --git a/19-coroutine/taxi_sim.py b/19-coroutine/taxi_sim.py deleted file mode 100644 index 6bf0ce9..0000000 --- a/19-coroutine/taxi_sim.py +++ /dev/null @@ -1,203 +0,0 @@ - -""" -Taxi simulator -============== - -Driving a taxi from the console:: - - >>> from taxi_sim import taxi_process - >>> taxi = taxi_process(ident=13, trips=2, start_time=0) - >>> next(taxi) - Event(time=0, proc=13, action='leave garage') - >>> taxi.send(_.time + 7) - Event(time=7, proc=13, action='pick up passenger') - >>> taxi.send(_.time + 23) - Event(time=30, proc=13, action='drop off passenger') - >>> taxi.send(_.time + 5) - Event(time=35, proc=13, action='pick up passenger') - >>> taxi.send(_.time + 48) - Event(time=83, proc=13, action='drop off passenger') - >>> taxi.send(_.time + 1) - Event(time=84, proc=13, action='going home') - >>> taxi.send(_.time + 10) - Traceback (most recent call last): - File "", line 1, in - StopIteration - -Sample run with two cars, random seed 10. This is a valid doctest:: - - >>> main(num_taxis=2, seed=10) - taxi: 0 Event(time=0, proc=0, action='leave garage') - taxi: 0 Event(time=5, proc=0, action='pick up passenger') - taxi: 1 Event(time=5, proc=1, action='leave garage') - taxi: 1 Event(time=10, proc=1, action='pick up passenger') - taxi: 1 Event(time=15, proc=1, action='drop off passenger') - taxi: 0 Event(time=17, proc=0, action='drop off passenger') - taxi: 1 Event(time=24, proc=1, action='pick up passenger') - taxi: 0 Event(time=26, proc=0, action='pick up passenger') - taxi: 0 Event(time=30, proc=0, action='drop off passenger') - taxi: 0 Event(time=34, proc=0, action='going home') - taxi: 1 Event(time=46, proc=1, action='drop off passenger') - taxi: 1 Event(time=48, proc=1, action='pick up passenger') - taxi: 1 Event(time=110, proc=1, action='drop off passenger') - taxi: 1 Event(time=139, proc=1, action='pick up passenger') - taxi: 1 Event(time=140, proc=1, action='drop off passenger') - taxi: 1 Event(time=150, proc=1, action='going home') - *** end of events *** - -See longer sample run at the end of this module. - -""" - -import argparse -import collections -import random -import queue - - -DEFAULT_NUMBER_OF_TAXIS = 3 -DEFAULT_END_TIME = 180 -SEARCH_DURATION = 5 -TRIP_DURATION = 20 -DEPARTURE_INTERVAL = 5 - -Event = collections.namedtuple('Event', 'time proc action') - - -# tag::TAXI_PROCESS[] -def taxi_process(ident, trips, start_time=0): # <1> - """Yield to simulator issuing event at each state change""" - time = yield Event(start_time, ident, 'leave garage') # <2> - for i in range(trips): # <3> - time = yield Event(time, ident, 'pick up passenger') # <4> - time = yield Event(time, ident, 'drop off passenger') # <5> - - yield Event(time, ident, 'going home') # <6> - # end of taxi process # <7> -# end::TAXI_PROCESS[] - - -# tag::TAXI_SIMULATOR[] -class Simulator: - - def __init__(self, procs_map): - self.events = queue.PriorityQueue() - self.procs = dict(procs_map) - - def run(self, end_time): # <1> - """Schedule and display events until time is up""" - # schedule the first event for each cab - for _, proc in sorted(self.procs.items()): # <2> - first_event = next(proc) # <3> - self.events.put(first_event) # <4> - - # main loop of the simulation - sim_time = 0 # <5> - while sim_time < end_time: # <6> - if self.events.empty(): # <7> - print('*** end of events ***') - break - - current_event = self.events.get() # <8> - sim_time, proc_id, previous_action = current_event # <9> - print('taxi:', proc_id, proc_id * ' ', current_event) # <10> - active_proc = self.procs[proc_id] # <11> - next_time = sim_time + compute_duration(previous_action) # <12> - try: - next_event = active_proc.send(next_time) # <13> - except StopIteration: - del self.procs[proc_id] # <14> - else: - self.events.put(next_event) # <15> - else: # <16> - msg = '*** end of simulation time: {} events pending ***' - print(msg.format(self.events.qsize())) -# end::TAXI_SIMULATOR[] - - -def compute_duration(previous_action): - """Compute action duration using exponential distribution""" - if previous_action in ['leave garage', 'drop off passenger']: - # new state is prowling - interval = SEARCH_DURATION - elif previous_action == 'pick up passenger': - # new state is trip - interval = TRIP_DURATION - elif previous_action == 'going home': - interval = 1 - else: - raise ValueError(f'Unknown previous_action: {previous_action}') - return int(random.expovariate(1 / interval)) + 1 - - -def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS, - seed=None): - """Initialize random generator, build procs and run simulation""" - if seed is not None: - random.seed(seed) # get reproducible results - - taxis = {i: taxi_process(i, (i + 1) * 2, i * DEPARTURE_INTERVAL) - for i in range(num_taxis)} - sim = Simulator(taxis) - sim.run(end_time) - - -if __name__ == '__main__': - - parser = argparse.ArgumentParser( - description='Taxi fleet simulator.') - parser.add_argument('-e', '--end-time', type=int, - default=DEFAULT_END_TIME, - help='simulation end time; default = %s' - % DEFAULT_END_TIME) - parser.add_argument('-t', '--taxis', type=int, - default=DEFAULT_NUMBER_OF_TAXIS, - help='number of taxis running; default = %s' - % DEFAULT_NUMBER_OF_TAXIS) - parser.add_argument('-s', '--seed', type=int, default=None, - help='random generator seed (for testing)') - - args = parser.parse_args() - main(args.end_time, args.taxis, args.seed) - - -""" - -Sample run from the command line, seed=3, maximum elapsed time=120:: - -# tag::TAXI_SAMPLE_RUN[] -$ python3 taxi_sim.py -s 3 -e 120 -taxi: 0 Event(time=0, proc=0, action='leave garage') -taxi: 0 Event(time=2, proc=0, action='pick up passenger') -taxi: 1 Event(time=5, proc=1, action='leave garage') -taxi: 1 Event(time=8, proc=1, action='pick up passenger') -taxi: 2 Event(time=10, proc=2, action='leave garage') -taxi: 2 Event(time=15, proc=2, action='pick up passenger') -taxi: 2 Event(time=17, proc=2, action='drop off passenger') -taxi: 0 Event(time=18, proc=0, action='drop off passenger') -taxi: 2 Event(time=18, proc=2, action='pick up passenger') -taxi: 2 Event(time=25, proc=2, action='drop off passenger') -taxi: 1 Event(time=27, proc=1, action='drop off passenger') -taxi: 2 Event(time=27, proc=2, action='pick up passenger') -taxi: 0 Event(time=28, proc=0, action='pick up passenger') -taxi: 2 Event(time=40, proc=2, action='drop off passenger') -taxi: 2 Event(time=44, proc=2, action='pick up passenger') -taxi: 1 Event(time=55, proc=1, action='pick up passenger') -taxi: 1 Event(time=59, proc=1, action='drop off passenger') -taxi: 0 Event(time=65, proc=0, action='drop off passenger') -taxi: 1 Event(time=65, proc=1, action='pick up passenger') -taxi: 2 Event(time=65, proc=2, action='drop off passenger') -taxi: 2 Event(time=72, proc=2, action='pick up passenger') -taxi: 0 Event(time=76, proc=0, action='going home') -taxi: 1 Event(time=80, proc=1, action='drop off passenger') -taxi: 1 Event(time=88, proc=1, action='pick up passenger') -taxi: 2 Event(time=95, proc=2, action='drop off passenger') -taxi: 2 Event(time=97, proc=2, action='pick up passenger') -taxi: 2 Event(time=98, proc=2, action='drop off passenger') -taxi: 1 Event(time=106, proc=1, action='drop off passenger') -taxi: 2 Event(time=109, proc=2, action='going home') -taxi: 1 Event(time=110, proc=1, action='going home') -*** end of events *** -# end::TAXI_SAMPLE_RUN[] - -""" diff --git a/19-coroutine/taxi_sim0.py b/19-coroutine/taxi_sim0.py deleted file mode 100644 index ed988f5..0000000 --- a/19-coroutine/taxi_sim0.py +++ /dev/null @@ -1,255 +0,0 @@ - -""" -Taxi simulator - -Sample run with two cars, random seed 10. This is a valid doctest. - - >>> main(num_taxis=2, seed=10) - taxi: 0 Event(time=0, proc=0, action='leave garage') - taxi: 0 Event(time=4, proc=0, action='pick up passenger') - taxi: 1 Event(time=5, proc=1, action='leave garage') - taxi: 1 Event(time=9, proc=1, action='pick up passenger') - taxi: 0 Event(time=10, proc=0, action='drop off passenger') - taxi: 1 Event(time=12, proc=1, action='drop off passenger') - taxi: 0 Event(time=17, proc=0, action='pick up passenger') - taxi: 1 Event(time=19, proc=1, action='pick up passenger') - taxi: 1 Event(time=21, proc=1, action='drop off passenger') - taxi: 1 Event(time=24, proc=1, action='pick up passenger') - taxi: 0 Event(time=28, proc=0, action='drop off passenger') - taxi: 1 Event(time=28, proc=1, action='drop off passenger') - taxi: 0 Event(time=29, proc=0, action='going home') - taxi: 1 Event(time=30, proc=1, action='pick up passenger') - taxi: 1 Event(time=61, proc=1, action='drop off passenger') - taxi: 1 Event(time=62, proc=1, action='going home') - *** end of events *** - -See explanation and longer sample run at the end of this module. - -""" - -import argparse -import collections -import queue -import random - -DEFAULT_NUMBER_OF_TAXIS = 3 -DEFAULT_END_TIME = 80 -SEARCH_DURATION = 4 -TRIP_DURATION = 10 -DEPARTURE_INTERVAL = 5 - -Event = collections.namedtuple('Event', 'time proc action') - - -def compute_delay(interval): - """Compute action delay using exponential distribution""" - return int(random.expovariate(1 / interval)) + 1 - -# BEGIN TAXI_PROCESS -def taxi_process(ident, trips, start_time=0): # <1> - """Yield to simulator issuing event at each state change""" - time = yield Event(start_time, ident, 'leave garage') # <2> - for i in range(trips): # <3> - prowling_ends = time + compute_delay(SEARCH_DURATION) # <4> - time = yield Event(prowling_ends, ident, 'pick up passenger') # <5> - - trip_ends = time + compute_delay(TRIP_DURATION) # <6> - time = yield Event(trip_ends, ident, 'drop off passenger') # <7> - - yield Event(time + 1, ident, 'going home') # <8> - # end of taxi process # <9> -# END TAXI_PROCESS - -# BEGIN TAXI_SIMULATOR -class Simulator: - - def __init__(self, procs_map): - self.events = queue.PriorityQueue() - self.procs = dict(procs_map) - - def run(self, end_time): # <1> - """Schedule and display events until time is up""" - # schedule the first event for each cab - for _, proc in sorted(self.procs.items()): # <2> - first_event = next(proc) # <3> - self.events.put(first_event) # <4> - - # main loop of the simulation - time = 0 - while time < end_time: # <5> - if self.events.empty(): # <6> - print('*** end of events ***') - break - - # get and display current event - current_event = self.events.get() # <7> - print('taxi:', current_event.proc, # <8> - current_event.proc * ' ', current_event) - - # schedule next action for current proc - time = current_event.time # <9> - proc = self.procs[current_event.proc] # <10> - try: - next_event = proc.send(time) # <11> - except StopIteration: - del self.procs[current_event.proc] # <12> - else: - self.events.put(next_event) # <13> - else: # <14> - msg = '*** end of simulation time: {} events pending ***' - print(msg.format(self.events.qsize())) -# END TAXI_SIMULATOR - -def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS, - seed=None): - """Initialize random generator, build procs and run simulation""" - if seed is not None: - random.seed(seed) # get reproducible results - - taxis = {i: taxi_process(i, (i + 1) * 2, i * DEPARTURE_INTERVAL) - for i in range(num_taxis)} - sim = Simulator(taxis) - sim.run(end_time) - - -if __name__ == '__main__': - - parser = argparse.ArgumentParser( - description='Taxi fleet simulator.') - parser.add_argument('-e', '--end-time', type=int, - default=DEFAULT_END_TIME, - help='simulation end time; default = %s' - % DEFAULT_END_TIME) - parser.add_argument('-t', '--taxis', type=int, - default=DEFAULT_NUMBER_OF_TAXIS, - help='number of taxis running; default = %s' - % DEFAULT_NUMBER_OF_TAXIS) - parser.add_argument('-s', '--seed', type=int, default=None, - help='random generator seed (for testing)') - - args = parser.parse_args() - main(args.end_time, args.taxis, args.seed) - - -""" -Notes for the ``taxi_process`` coroutine:: - -<1> `taxi_process` will be called once per taxi, creating a generator - object to represent its operations. `ident` is the number of the taxi - (eg. 0, 1, 2 in the sample run); `trips` is the number of trips this - taxi will make before going home; `start_time` is when the taxi - leaves the garage. - -<2> The first `Event` yielded is `'leave garage'`. This suspends the - coroutine, and lets the simulation main loop proceed to the next - scheduled event. When it's time to reactivate this process, the main - loop will `send` the current simulation time, which is assigned to - `time`. - -<3> This block will be repeated once for each trip. - -<4> The ending time of the search for a passenger is computed. - -<5> An `Event` signaling passenger pick up is yielded. The coroutine - pauses here. When the time comes to reactivate this coroutine, - the main loop will again `send` the current time. - -<6> The ending time of the trip is computed, taking into account the - current `time`. - -<7> An `Event` signaling passenger drop off is yielded. Coroutine - suspended again, waiting for the main loop to send the time of when - it's time to continue. - -<8> The `for` loop ends after the given number of trips, and a final - `'going home'` event is yielded, to happen 1 minute after the current - time. The coroutine will suspend for the last time. When reactivated, - it will be sent the time from the simulation main loop, but here I - don't assign it to any variable because it will not be useful. - -<9> When the coroutine falls off the end, the coroutine object raises - `StopIteration`. - - -Notes for the ``Simulator.run`` method:: - -<1> The simulation `end_time` is the only required argument for `run`. - -<2> Use `sorted` to retrieve the `self.procs` items ordered by the - integer key; we don't care about the key, so assign it to `_`. - -<3> `next(proc)` primes each coroutine by advancing it to the first - yield, so it's ready to be sent data. An `Event` is yielded. - -<4> Add each event to the `self.events` `PriorityQueue`. The first - event for each taxi is `'leave garage'`, as seen in the sample run - (ex_taxi_process>>). - -<5> Main loop of the simulation: run until the current `time` equals - or exceeds the `end_time`. - -<6> The main loop may also exit if there are no pending events in the - queue. - -<7> Get `Event` with the smallest `time` in the queue; this is the - `current_event`. - -<8> Display the `Event`, identifying the taxi and adding indentation - according to the taxi id. - -<9> Update the simulation time with the time of the `current_event`. - -<10> Retrieve the coroutine for this taxi from the `self.procs` - dictionary. - -<11> Send the `time` to the coroutine. The coroutine will yield the - `next_event` or raise `StopIteration` it's finished. - -<12> If `StopIteration` was raised, delete the coroutine from the - `self.procs` dictionary. - -<13> Otherwise, put the `next_event` in the queue. - -<14> If the loop exits because the simulation time passed, display the - number of events pending (which may be zero by coincidence, - sometimes). - - -Sample run from the command line, seed=24, total elapsed time=160:: - -# BEGIN TAXI_SAMPLE_RUN -$ python3 taxi_sim.py -s 24 -e 160 -taxi: 0 Event(time=0, proc=0, action='leave garage') -taxi: 0 Event(time=5, proc=0, action='pick up passenger') -taxi: 1 Event(time=5, proc=1, action='leave garage') -taxi: 1 Event(time=6, proc=1, action='pick up passenger') -taxi: 2 Event(time=10, proc=2, action='leave garage') -taxi: 2 Event(time=11, proc=2, action='pick up passenger') -taxi: 2 Event(time=23, proc=2, action='drop off passenger') -taxi: 0 Event(time=24, proc=0, action='drop off passenger') -taxi: 2 Event(time=24, proc=2, action='pick up passenger') -taxi: 2 Event(time=26, proc=2, action='drop off passenger') -taxi: 0 Event(time=30, proc=0, action='pick up passenger') -taxi: 2 Event(time=31, proc=2, action='pick up passenger') -taxi: 0 Event(time=43, proc=0, action='drop off passenger') -taxi: 0 Event(time=44, proc=0, action='going home') -taxi: 2 Event(time=46, proc=2, action='drop off passenger') -taxi: 2 Event(time=49, proc=2, action='pick up passenger') -taxi: 1 Event(time=70, proc=1, action='drop off passenger') -taxi: 2 Event(time=70, proc=2, action='drop off passenger') -taxi: 2 Event(time=71, proc=2, action='pick up passenger') -taxi: 2 Event(time=79, proc=2, action='drop off passenger') -taxi: 1 Event(time=88, proc=1, action='pick up passenger') -taxi: 2 Event(time=92, proc=2, action='pick up passenger') -taxi: 2 Event(time=98, proc=2, action='drop off passenger') -taxi: 2 Event(time=99, proc=2, action='going home') -taxi: 1 Event(time=102, proc=1, action='drop off passenger') -taxi: 1 Event(time=104, proc=1, action='pick up passenger') -taxi: 1 Event(time=135, proc=1, action='drop off passenger') -taxi: 1 Event(time=136, proc=1, action='pick up passenger') -taxi: 1 Event(time=151, proc=1, action='drop off passenger') -taxi: 1 Event(time=152, proc=1, action='going home') -*** end of events *** -# END TAXI_SAMPLE_RUN - -""" diff --git a/19-coroutine/taxi_sim_delay.py b/19-coroutine/taxi_sim_delay.py deleted file mode 100644 index 8f38e4f..0000000 --- a/19-coroutine/taxi_sim_delay.py +++ /dev/null @@ -1,215 +0,0 @@ - -""" -Taxi simulator with delay on output -=================================== - -This is a variation of ``taxi_sim.py`` which adds a ``-d`` comand-line -option. When given, that option adds a delay in the main loop, pausing -the simulation for .5s for each "minute" of simulation time. - - -Driving a taxi from the console:: - - >>> from taxi_sim import taxi_process - >>> taxi = taxi_process(ident=13, trips=2, start_time=0) - >>> next(taxi) - Event(time=0, proc=13, action='leave garage') - >>> taxi.send(_.time + 7) - Event(time=7, proc=13, action='pick up passenger') - >>> taxi.send(_.time + 23) - Event(time=30, proc=13, action='drop off passenger') - >>> taxi.send(_.time + 5) - Event(time=35, proc=13, action='pick up passenger') - >>> taxi.send(_.time + 48) - Event(time=83, proc=13, action='drop off passenger') - >>> taxi.send(_.time + 1) - Event(time=84, proc=13, action='going home') - >>> taxi.send(_.time + 10) - Traceback (most recent call last): - File "", line 1, in - StopIteration - -Sample run with two cars, random seed 10. This is a valid doctest:: - - >>> main(num_taxis=2, seed=10) - taxi: 0 Event(time=0, proc=0, action='leave garage') - taxi: 0 Event(time=5, proc=0, action='pick up passenger') - taxi: 1 Event(time=5, proc=1, action='leave garage') - taxi: 1 Event(time=10, proc=1, action='pick up passenger') - taxi: 1 Event(time=15, proc=1, action='drop off passenger') - taxi: 0 Event(time=17, proc=0, action='drop off passenger') - taxi: 1 Event(time=24, proc=1, action='pick up passenger') - taxi: 0 Event(time=26, proc=0, action='pick up passenger') - taxi: 0 Event(time=30, proc=0, action='drop off passenger') - taxi: 0 Event(time=34, proc=0, action='going home') - taxi: 1 Event(time=46, proc=1, action='drop off passenger') - taxi: 1 Event(time=48, proc=1, action='pick up passenger') - taxi: 1 Event(time=110, proc=1, action='drop off passenger') - taxi: 1 Event(time=139, proc=1, action='pick up passenger') - taxi: 1 Event(time=140, proc=1, action='drop off passenger') - taxi: 1 Event(time=150, proc=1, action='going home') - *** end of events *** - -See longer sample run at the end of this module. - -""" - -import random -import collections -import queue -import argparse -import time - -DEFAULT_NUMBER_OF_TAXIS = 3 -DEFAULT_END_TIME = 180 -SEARCH_DURATION = 5 -TRIP_DURATION = 20 -DEPARTURE_INTERVAL = 5 - -Event = collections.namedtuple('Event', 'time proc action') - - -# BEGIN TAXI_PROCESS -def taxi_process(ident, trips, start_time=0): # <1> - """Yield to simulator issuing event at each state change""" - time = yield Event(start_time, ident, 'leave garage') # <2> - for i in range(trips): # <3> - time = yield Event(time, ident, 'pick up passenger') # <4> - time = yield Event(time, ident, 'drop off passenger') # <5> - - yield Event(time, ident, 'going home') # <6> - # end of taxi process # <7> -# END TAXI_PROCESS - - -# BEGIN TAXI_SIMULATOR -class Simulator: - - def __init__(self, procs_map): - self.events = queue.PriorityQueue() - self.procs = dict(procs_map) - - def run(self, end_time, delay=False): # <1> - """Schedule and display events until time is up""" - # schedule the first event for each cab - for _, proc in sorted(self.procs.items()): # <2> - first_event = next(proc) # <3> - self.events.put(first_event) # <4> - - # main loop of the simulation - sim_time = 0 # <5> - while sim_time < end_time: # <6> - if self.events.empty(): # <7> - print('*** end of events ***') - break - - # get and display current event - current_event = self.events.get() # <8> - if delay: - time.sleep((current_event.time - sim_time) / 2) - # update the simulation time - sim_time, proc_id, previous_action = current_event - print('taxi:', proc_id, proc_id * ' ', current_event) - active_proc = self.procs[proc_id] - # schedule next action for current proc - next_time = sim_time + compute_duration(previous_action) - try: - next_event = active_proc.send(next_time) # <12> - except StopIteration: - del self.procs[proc_id] # <13> - else: - self.events.put(next_event) # <14> - else: # <15> - msg = '*** end of simulation time: {} events pending ***' - print(msg.format(self.events.qsize())) -# END TAXI_SIMULATOR - - -def compute_duration(previous_action): - """Compute action duration using exponential distribution""" - if previous_action in ['leave garage', 'drop off passenger']: - # new state is prowling - interval = SEARCH_DURATION - elif previous_action == 'pick up passenger': - # new state is trip - interval = TRIP_DURATION - elif previous_action == 'going home': - interval = 1 - else: - raise ValueError('Unknown previous_action: %s' % previous_action) - return int(random.expovariate(1/interval)) + 1 - - -def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS, - seed=None, delay=False): - """Initialize random generator, build procs and run simulation""" - if seed is not None: - random.seed(seed) # get reproducible results - - taxis = {i: taxi_process(i, (i+1)*2, i*DEPARTURE_INTERVAL) - for i in range(num_taxis)} - sim = Simulator(taxis) - sim.run(end_time, delay) - - -if __name__ == '__main__': - - parser = argparse.ArgumentParser( - description='Taxi fleet simulator.') - parser.add_argument('-e', '--end-time', type=int, - default=DEFAULT_END_TIME, - help='simulation end time; default = %s' - % DEFAULT_END_TIME) - parser.add_argument('-t', '--taxis', type=int, - default=DEFAULT_NUMBER_OF_TAXIS, - help='number of taxis running; default = %s' - % DEFAULT_NUMBER_OF_TAXIS) - parser.add_argument('-s', '--seed', type=int, default=None, - help='random generator seed (for testing)') - parser.add_argument('-d', '--delay', action='store_true', - help='introduce delay proportional to simulation time') - - args = parser.parse_args() - main(args.end_time, args.taxis, args.seed, args.delay) - - -""" - -Sample run from the command line, seed=3, maximum elapsed time=120:: - -# BEGIN TAXI_SAMPLE_RUN -$ python3 taxi_sim.py -s 3 -e 120 -taxi: 0 Event(time=0, proc=0, action='leave garage') -taxi: 0 Event(time=2, proc=0, action='pick up passenger') -taxi: 1 Event(time=5, proc=1, action='leave garage') -taxi: 1 Event(time=8, proc=1, action='pick up passenger') -taxi: 2 Event(time=10, proc=2, action='leave garage') -taxi: 2 Event(time=15, proc=2, action='pick up passenger') -taxi: 2 Event(time=17, proc=2, action='drop off passenger') -taxi: 0 Event(time=18, proc=0, action='drop off passenger') -taxi: 2 Event(time=18, proc=2, action='pick up passenger') -taxi: 2 Event(time=25, proc=2, action='drop off passenger') -taxi: 1 Event(time=27, proc=1, action='drop off passenger') -taxi: 2 Event(time=27, proc=2, action='pick up passenger') -taxi: 0 Event(time=28, proc=0, action='pick up passenger') -taxi: 2 Event(time=40, proc=2, action='drop off passenger') -taxi: 2 Event(time=44, proc=2, action='pick up passenger') -taxi: 1 Event(time=55, proc=1, action='pick up passenger') -taxi: 1 Event(time=59, proc=1, action='drop off passenger') -taxi: 0 Event(time=65, proc=0, action='drop off passenger') -taxi: 1 Event(time=65, proc=1, action='pick up passenger') -taxi: 2 Event(time=65, proc=2, action='drop off passenger') -taxi: 2 Event(time=72, proc=2, action='pick up passenger') -taxi: 0 Event(time=76, proc=0, action='going home') -taxi: 1 Event(time=80, proc=1, action='drop off passenger') -taxi: 1 Event(time=88, proc=1, action='pick up passenger') -taxi: 2 Event(time=95, proc=2, action='drop off passenger') -taxi: 2 Event(time=97, proc=2, action='pick up passenger') -taxi: 2 Event(time=98, proc=2, action='drop off passenger') -taxi: 1 Event(time=106, proc=1, action='drop off passenger') -taxi: 2 Event(time=109, proc=2, action='going home') -taxi: 1 Event(time=110, proc=1, action='going home') -*** end of events *** -# END TAXI_SAMPLE_RUN - -""" diff --git a/19-coroutine/yield_from_expansion.py b/19-coroutine/yield_from_expansion.py deleted file mode 100644 index 837925f..0000000 --- a/19-coroutine/yield_from_expansion.py +++ /dev/null @@ -1,52 +0,0 @@ -# Code below is the expansion of the statement: -# -# RESULT = yield from EXPR -# -# Copied verbatim from the Formal Semantics section of -# PEP 380 -- Syntax for Delegating to a Subgenerator -# -# https://www.python.org/dev/peps/pep-0380/#formal-semantics - - -# tag::YIELD_FROM_EXPANSION[] -_i = iter(EXPR) # <1> -try: - _y = next(_i) # <2> -except StopIteration as _e: - _r = _e.value # <3> -else: - while 1: # <4> - try: - _s = yield _y # <5> - except GeneratorExit as _e: # <6> - try: - _m = _i.close - except AttributeError: - pass - else: - _m() - raise _e - except BaseException as _e: # <7> - _x = sys.exc_info() - try: - _m = _i.throw - except AttributeError: - raise _e - else: # <8> - try: - _y = _m(*_x) - except StopIteration as _e: - _r = _e.value - break - else: # <9> - try: # <10> - if _s is None: # <11> - _y = next(_i) - else: - _y = _i.send(_s) - except StopIteration as _e: # <12> - _r = _e.value - break - -RESULT = _r # <13> -# end::YIELD_FROM_EXPANSION[] diff --git a/19-coroutine/yield_from_expansion_simplified.py b/19-coroutine/yield_from_expansion_simplified.py deleted file mode 100644 index 7648da5..0000000 --- a/19-coroutine/yield_from_expansion_simplified.py +++ /dev/null @@ -1,32 +0,0 @@ -# Code below is a very simplified expansion of the statement: -# -# RESULT = yield from EXPR -# -# This code assumes that the subgenerator will run to completion, -# without the client ever calling ``.throw()`` or ``.close()``. -# Also, this code makes no distinction between the client -# calling ``next(subgen)`` or ``subgen.send(...)`` -# -# The full expansion is in: -# PEP 380 -- Syntax for Delegating to a Subgenerator -# -# https://www.python.org/dev/peps/pep-0380/#formal-semantics - - -# tag::YIELD_FROM_EXPANSION_SIMPLIFIED[] -_i = iter(EXPR) # <1> -try: - _y = next(_i) # <2> -except StopIteration as _e: - _r = _e.value # <3> -else: - while 1: # <4> - _s = yield _y # <5> - try: - _y = _i.send(_s) # <6> - except StopIteration as _e: # <7> - _r = _e.value - break - -RESULT = _r # <8> -# end::YIELD_FROM_EXPANSION_SIMPLIFIED[] diff --git a/21-futures/demo_executor_map.py b/20-futures/demo_executor_map.py similarity index 100% rename from 21-futures/demo_executor_map.py rename to 20-futures/demo_executor_map.py diff --git a/21-futures/getflags/.gitignore b/20-futures/getflags/.gitignore similarity index 100% rename from 21-futures/getflags/.gitignore rename to 20-futures/getflags/.gitignore diff --git a/21-futures/getflags/country_codes.txt b/20-futures/getflags/country_codes.txt similarity index 100% rename from 21-futures/getflags/country_codes.txt rename to 20-futures/getflags/country_codes.txt diff --git a/21-futures/getflags/downloaded/.gitignore b/20-futures/getflags/downloaded/.gitignore similarity index 100% rename from 21-futures/getflags/downloaded/.gitignore rename to 20-futures/getflags/downloaded/.gitignore diff --git a/21-futures/getflags/flags.py b/20-futures/getflags/flags.py similarity index 100% rename from 21-futures/getflags/flags.py rename to 20-futures/getflags/flags.py diff --git a/21-futures/getflags/flags.zip b/20-futures/getflags/flags.zip similarity index 100% rename from 21-futures/getflags/flags.zip rename to 20-futures/getflags/flags.zip diff --git a/21-futures/getflags/flags2_asyncio.py b/20-futures/getflags/flags2_asyncio.py similarity index 100% rename from 21-futures/getflags/flags2_asyncio.py rename to 20-futures/getflags/flags2_asyncio.py diff --git a/21-futures/getflags/flags2_asyncio_executor.py b/20-futures/getflags/flags2_asyncio_executor.py similarity index 100% rename from 21-futures/getflags/flags2_asyncio_executor.py rename to 20-futures/getflags/flags2_asyncio_executor.py diff --git a/21-futures/getflags/flags2_common.py b/20-futures/getflags/flags2_common.py similarity index 100% rename from 21-futures/getflags/flags2_common.py rename to 20-futures/getflags/flags2_common.py diff --git a/21-futures/getflags/flags2_sequential.py b/20-futures/getflags/flags2_sequential.py similarity index 100% rename from 21-futures/getflags/flags2_sequential.py rename to 20-futures/getflags/flags2_sequential.py diff --git a/21-futures/getflags/flags2_threadpool.py b/20-futures/getflags/flags2_threadpool.py similarity index 100% rename from 21-futures/getflags/flags2_threadpool.py rename to 20-futures/getflags/flags2_threadpool.py diff --git a/21-futures/getflags/flags3_asyncio.py b/20-futures/getflags/flags3_asyncio.py similarity index 100% rename from 21-futures/getflags/flags3_asyncio.py rename to 20-futures/getflags/flags3_asyncio.py diff --git a/21-futures/getflags/flags_asyncio.py b/20-futures/getflags/flags_asyncio.py similarity index 100% rename from 21-futures/getflags/flags_asyncio.py rename to 20-futures/getflags/flags_asyncio.py diff --git a/21-futures/getflags/flags_threadpool.py b/20-futures/getflags/flags_threadpool.py similarity index 100% rename from 21-futures/getflags/flags_threadpool.py rename to 20-futures/getflags/flags_threadpool.py diff --git a/21-futures/getflags/flags_threadpool_futures.py b/20-futures/getflags/flags_threadpool_futures.py similarity index 100% rename from 21-futures/getflags/flags_threadpool_futures.py rename to 20-futures/getflags/flags_threadpool_futures.py diff --git a/21-futures/getflags/requirements.txt b/20-futures/getflags/requirements.txt similarity index 100% rename from 21-futures/getflags/requirements.txt rename to 20-futures/getflags/requirements.txt diff --git a/21-futures/getflags/slow_server.py b/20-futures/getflags/slow_server.py similarity index 100% rename from 21-futures/getflags/slow_server.py rename to 20-futures/getflags/slow_server.py diff --git a/21-futures/primes/primes.py b/20-futures/primes/primes.py similarity index 100% rename from 21-futures/primes/primes.py rename to 20-futures/primes/primes.py diff --git a/21-futures/primes/proc_pool.py b/20-futures/primes/proc_pool.py similarity index 100% rename from 21-futures/primes/proc_pool.py rename to 20-futures/primes/proc_pool.py diff --git a/22-async/README.rst b/21-async/README.rst similarity index 100% rename from 22-async/README.rst rename to 21-async/README.rst diff --git a/22-async/domains/README.rst b/21-async/domains/README.rst similarity index 100% rename from 22-async/domains/README.rst rename to 21-async/domains/README.rst diff --git a/22-async/domains/asyncio/blogdom.py b/21-async/domains/asyncio/blogdom.py similarity index 100% rename from 22-async/domains/asyncio/blogdom.py rename to 21-async/domains/asyncio/blogdom.py diff --git a/22-async/domains/asyncio/domaincheck.py b/21-async/domains/asyncio/domaincheck.py similarity index 100% rename from 22-async/domains/asyncio/domaincheck.py rename to 21-async/domains/asyncio/domaincheck.py diff --git a/22-async/domains/asyncio/domainlib.py b/21-async/domains/asyncio/domainlib.py similarity index 100% rename from 22-async/domains/asyncio/domainlib.py rename to 21-async/domains/asyncio/domainlib.py diff --git a/22-async/domains/curio/blogdom.py b/21-async/domains/curio/blogdom.py similarity index 100% rename from 22-async/domains/curio/blogdom.py rename to 21-async/domains/curio/blogdom.py diff --git a/22-async/domains/curio/domaincheck.py b/21-async/domains/curio/domaincheck.py similarity index 100% rename from 22-async/domains/curio/domaincheck.py rename to 21-async/domains/curio/domaincheck.py diff --git a/22-async/domains/curio/domainlib.py b/21-async/domains/curio/domainlib.py similarity index 100% rename from 22-async/domains/curio/domainlib.py rename to 21-async/domains/curio/domainlib.py diff --git a/22-async/domains/curio/requirements.txt b/21-async/domains/curio/requirements.txt similarity index 100% rename from 22-async/domains/curio/requirements.txt rename to 21-async/domains/curio/requirements.txt diff --git a/22-async/mojifinder/README.md b/21-async/mojifinder/README.md similarity index 100% rename from 22-async/mojifinder/README.md rename to 21-async/mojifinder/README.md diff --git a/22-async/mojifinder/bottle.py b/21-async/mojifinder/bottle.py similarity index 100% rename from 22-async/mojifinder/bottle.py rename to 21-async/mojifinder/bottle.py diff --git a/22-async/mojifinder/charindex.py b/21-async/mojifinder/charindex.py similarity index 100% rename from 22-async/mojifinder/charindex.py rename to 21-async/mojifinder/charindex.py diff --git a/22-async/mojifinder/requirements.txt b/21-async/mojifinder/requirements.txt similarity index 100% rename from 22-async/mojifinder/requirements.txt rename to 21-async/mojifinder/requirements.txt diff --git a/22-async/mojifinder/static/form.html b/21-async/mojifinder/static/form.html similarity index 100% rename from 22-async/mojifinder/static/form.html rename to 21-async/mojifinder/static/form.html diff --git a/22-async/mojifinder/tcp_mojifinder.py b/21-async/mojifinder/tcp_mojifinder.py similarity index 100% rename from 22-async/mojifinder/tcp_mojifinder.py rename to 21-async/mojifinder/tcp_mojifinder.py diff --git a/22-async/mojifinder/web_mojifinder.py b/21-async/mojifinder/web_mojifinder.py similarity index 100% rename from 22-async/mojifinder/web_mojifinder.py rename to 21-async/mojifinder/web_mojifinder.py diff --git a/22-async/mojifinder/web_mojifinder_bottle.py b/21-async/mojifinder/web_mojifinder_bottle.py similarity index 100% rename from 22-async/mojifinder/web_mojifinder_bottle.py rename to 21-async/mojifinder/web_mojifinder_bottle.py diff --git a/23-dyn-attr-prop/README.rst b/22-dyn-attr-prop/README.rst similarity index 100% rename from 23-dyn-attr-prop/README.rst rename to 22-dyn-attr-prop/README.rst diff --git a/23-dyn-attr-prop/blackknight.py b/22-dyn-attr-prop/blackknight.py similarity index 100% rename from 23-dyn-attr-prop/blackknight.py rename to 22-dyn-attr-prop/blackknight.py diff --git a/23-dyn-attr-prop/bulkfood/bulkfood_v1.py b/22-dyn-attr-prop/bulkfood/bulkfood_v1.py similarity index 100% rename from 23-dyn-attr-prop/bulkfood/bulkfood_v1.py rename to 22-dyn-attr-prop/bulkfood/bulkfood_v1.py diff --git a/23-dyn-attr-prop/bulkfood/bulkfood_v2.py b/22-dyn-attr-prop/bulkfood/bulkfood_v2.py similarity index 100% rename from 23-dyn-attr-prop/bulkfood/bulkfood_v2.py rename to 22-dyn-attr-prop/bulkfood/bulkfood_v2.py diff --git a/23-dyn-attr-prop/bulkfood/bulkfood_v2b.py b/22-dyn-attr-prop/bulkfood/bulkfood_v2b.py similarity index 100% rename from 23-dyn-attr-prop/bulkfood/bulkfood_v2b.py rename to 22-dyn-attr-prop/bulkfood/bulkfood_v2b.py diff --git a/23-dyn-attr-prop/bulkfood/bulkfood_v2prop.py b/22-dyn-attr-prop/bulkfood/bulkfood_v2prop.py similarity index 100% rename from 23-dyn-attr-prop/bulkfood/bulkfood_v2prop.py rename to 22-dyn-attr-prop/bulkfood/bulkfood_v2prop.py diff --git a/23-dyn-attr-prop/doc_property.py b/22-dyn-attr-prop/doc_property.py similarity index 100% rename from 23-dyn-attr-prop/doc_property.py rename to 22-dyn-attr-prop/doc_property.py diff --git a/23-dyn-attr-prop/oscon/data/osconfeed.json b/22-dyn-attr-prop/oscon/data/osconfeed.json similarity index 100% rename from 23-dyn-attr-prop/oscon/data/osconfeed.json rename to 22-dyn-attr-prop/oscon/data/osconfeed.json diff --git a/23-dyn-attr-prop/oscon/demo_schedule2.py b/22-dyn-attr-prop/oscon/demo_schedule2.py similarity index 100% rename from 23-dyn-attr-prop/oscon/demo_schedule2.py rename to 22-dyn-attr-prop/oscon/demo_schedule2.py diff --git a/23-dyn-attr-prop/oscon/explore0.py b/22-dyn-attr-prop/oscon/explore0.py similarity index 100% rename from 23-dyn-attr-prop/oscon/explore0.py rename to 22-dyn-attr-prop/oscon/explore0.py diff --git a/23-dyn-attr-prop/oscon/explore1.py b/22-dyn-attr-prop/oscon/explore1.py similarity index 100% rename from 23-dyn-attr-prop/oscon/explore1.py rename to 22-dyn-attr-prop/oscon/explore1.py diff --git a/23-dyn-attr-prop/oscon/explore2.py b/22-dyn-attr-prop/oscon/explore2.py similarity index 100% rename from 23-dyn-attr-prop/oscon/explore2.py rename to 22-dyn-attr-prop/oscon/explore2.py diff --git a/23-dyn-attr-prop/oscon/osconfeed-sample.json b/22-dyn-attr-prop/oscon/osconfeed-sample.json similarity index 100% rename from 23-dyn-attr-prop/oscon/osconfeed-sample.json rename to 22-dyn-attr-prop/oscon/osconfeed-sample.json diff --git a/23-dyn-attr-prop/oscon/osconfeed_explore.rst b/22-dyn-attr-prop/oscon/osconfeed_explore.rst similarity index 100% rename from 23-dyn-attr-prop/oscon/osconfeed_explore.rst rename to 22-dyn-attr-prop/oscon/osconfeed_explore.rst diff --git a/23-dyn-attr-prop/oscon/runtests.sh b/22-dyn-attr-prop/oscon/runtests.sh similarity index 100% rename from 23-dyn-attr-prop/oscon/runtests.sh rename to 22-dyn-attr-prop/oscon/runtests.sh diff --git a/23-dyn-attr-prop/oscon/schedule_v1.py b/22-dyn-attr-prop/oscon/schedule_v1.py similarity index 100% rename from 23-dyn-attr-prop/oscon/schedule_v1.py rename to 22-dyn-attr-prop/oscon/schedule_v1.py diff --git a/23-dyn-attr-prop/oscon/schedule_v2.py b/22-dyn-attr-prop/oscon/schedule_v2.py similarity index 100% rename from 23-dyn-attr-prop/oscon/schedule_v2.py rename to 22-dyn-attr-prop/oscon/schedule_v2.py diff --git a/23-dyn-attr-prop/oscon/schedule_v3.py b/22-dyn-attr-prop/oscon/schedule_v3.py similarity index 100% rename from 23-dyn-attr-prop/oscon/schedule_v3.py rename to 22-dyn-attr-prop/oscon/schedule_v3.py diff --git a/23-dyn-attr-prop/oscon/schedule_v4.py b/22-dyn-attr-prop/oscon/schedule_v4.py similarity index 100% rename from 23-dyn-attr-prop/oscon/schedule_v4.py rename to 22-dyn-attr-prop/oscon/schedule_v4.py diff --git a/23-dyn-attr-prop/oscon/schedule_v4_hasattr.py b/22-dyn-attr-prop/oscon/schedule_v4_hasattr.py similarity index 100% rename from 23-dyn-attr-prop/oscon/schedule_v4_hasattr.py rename to 22-dyn-attr-prop/oscon/schedule_v4_hasattr.py diff --git a/23-dyn-attr-prop/oscon/schedule_v5.py b/22-dyn-attr-prop/oscon/schedule_v5.py similarity index 100% rename from 23-dyn-attr-prop/oscon/schedule_v5.py rename to 22-dyn-attr-prop/oscon/schedule_v5.py diff --git a/23-dyn-attr-prop/oscon/test_schedule_v1.py b/22-dyn-attr-prop/oscon/test_schedule_v1.py similarity index 100% rename from 23-dyn-attr-prop/oscon/test_schedule_v1.py rename to 22-dyn-attr-prop/oscon/test_schedule_v1.py diff --git a/23-dyn-attr-prop/oscon/test_schedule_v2.py b/22-dyn-attr-prop/oscon/test_schedule_v2.py similarity index 100% rename from 23-dyn-attr-prop/oscon/test_schedule_v2.py rename to 22-dyn-attr-prop/oscon/test_schedule_v2.py diff --git a/23-dyn-attr-prop/oscon/test_schedule_v3.py b/22-dyn-attr-prop/oscon/test_schedule_v3.py similarity index 100% rename from 23-dyn-attr-prop/oscon/test_schedule_v3.py rename to 22-dyn-attr-prop/oscon/test_schedule_v3.py diff --git a/23-dyn-attr-prop/oscon/test_schedule_v4.py b/22-dyn-attr-prop/oscon/test_schedule_v4.py similarity index 100% rename from 23-dyn-attr-prop/oscon/test_schedule_v4.py rename to 22-dyn-attr-prop/oscon/test_schedule_v4.py diff --git a/23-dyn-attr-prop/oscon/test_schedule_v5.py b/22-dyn-attr-prop/oscon/test_schedule_v5.py similarity index 100% rename from 23-dyn-attr-prop/oscon/test_schedule_v5.py rename to 22-dyn-attr-prop/oscon/test_schedule_v5.py diff --git a/23-dyn-attr-prop/pseudo_construction.py b/22-dyn-attr-prop/pseudo_construction.py similarity index 100% rename from 23-dyn-attr-prop/pseudo_construction.py rename to 22-dyn-attr-prop/pseudo_construction.py diff --git a/24-descriptor/README.rst b/23-descriptor/README.rst similarity index 100% rename from 24-descriptor/README.rst rename to 23-descriptor/README.rst diff --git a/24-descriptor/bulkfood/bulkfood_v3.py b/23-descriptor/bulkfood/bulkfood_v3.py similarity index 100% rename from 24-descriptor/bulkfood/bulkfood_v3.py rename to 23-descriptor/bulkfood/bulkfood_v3.py diff --git a/24-descriptor/bulkfood/bulkfood_v4.py b/23-descriptor/bulkfood/bulkfood_v4.py similarity index 100% rename from 24-descriptor/bulkfood/bulkfood_v4.py rename to 23-descriptor/bulkfood/bulkfood_v4.py diff --git a/24-descriptor/bulkfood/bulkfood_v4c.py b/23-descriptor/bulkfood/bulkfood_v4c.py similarity index 100% rename from 24-descriptor/bulkfood/bulkfood_v4c.py rename to 23-descriptor/bulkfood/bulkfood_v4c.py diff --git a/24-descriptor/bulkfood/bulkfood_v5.py b/23-descriptor/bulkfood/bulkfood_v5.py similarity index 100% rename from 24-descriptor/bulkfood/bulkfood_v5.py rename to 23-descriptor/bulkfood/bulkfood_v5.py diff --git a/24-descriptor/bulkfood/model_v4c.py b/23-descriptor/bulkfood/model_v4c.py similarity index 100% rename from 24-descriptor/bulkfood/model_v4c.py rename to 23-descriptor/bulkfood/model_v4c.py diff --git a/24-descriptor/bulkfood/model_v5.py b/23-descriptor/bulkfood/model_v5.py similarity index 100% rename from 24-descriptor/bulkfood/model_v5.py rename to 23-descriptor/bulkfood/model_v5.py diff --git a/24-descriptor/descriptorkinds.py b/23-descriptor/descriptorkinds.py similarity index 100% rename from 24-descriptor/descriptorkinds.py rename to 23-descriptor/descriptorkinds.py diff --git a/24-descriptor/descriptorkinds_dump.py b/23-descriptor/descriptorkinds_dump.py similarity index 100% rename from 24-descriptor/descriptorkinds_dump.py rename to 23-descriptor/descriptorkinds_dump.py diff --git a/24-descriptor/method_is_descriptor.py b/23-descriptor/method_is_descriptor.py similarity index 100% rename from 24-descriptor/method_is_descriptor.py rename to 23-descriptor/method_is_descriptor.py diff --git a/25-class-metaprog/autoconst/autoconst.py b/24-class-metaprog/autoconst/autoconst.py similarity index 100% rename from 25-class-metaprog/autoconst/autoconst.py rename to 24-class-metaprog/autoconst/autoconst.py diff --git a/25-class-metaprog/autoconst/autoconst_demo.py b/24-class-metaprog/autoconst/autoconst_demo.py similarity index 100% rename from 25-class-metaprog/autoconst/autoconst_demo.py rename to 24-class-metaprog/autoconst/autoconst_demo.py diff --git a/25-class-metaprog/bulkfood/README.md b/24-class-metaprog/bulkfood/README.md similarity index 100% rename from 25-class-metaprog/bulkfood/README.md rename to 24-class-metaprog/bulkfood/README.md diff --git a/25-class-metaprog/bulkfood/bulkfood_v6.py b/24-class-metaprog/bulkfood/bulkfood_v6.py similarity index 100% rename from 25-class-metaprog/bulkfood/bulkfood_v6.py rename to 24-class-metaprog/bulkfood/bulkfood_v6.py diff --git a/25-class-metaprog/bulkfood/bulkfood_v7.py b/24-class-metaprog/bulkfood/bulkfood_v7.py similarity index 100% rename from 25-class-metaprog/bulkfood/bulkfood_v7.py rename to 24-class-metaprog/bulkfood/bulkfood_v7.py diff --git a/25-class-metaprog/bulkfood/bulkfood_v8.py b/24-class-metaprog/bulkfood/bulkfood_v8.py similarity index 100% rename from 25-class-metaprog/bulkfood/bulkfood_v8.py rename to 24-class-metaprog/bulkfood/bulkfood_v8.py diff --git a/25-class-metaprog/bulkfood/model_v6.py b/24-class-metaprog/bulkfood/model_v6.py similarity index 100% rename from 25-class-metaprog/bulkfood/model_v6.py rename to 24-class-metaprog/bulkfood/model_v6.py diff --git a/25-class-metaprog/bulkfood/model_v7.py b/24-class-metaprog/bulkfood/model_v7.py similarity index 100% rename from 25-class-metaprog/bulkfood/model_v7.py rename to 24-class-metaprog/bulkfood/model_v7.py diff --git a/25-class-metaprog/bulkfood/model_v8.py b/24-class-metaprog/bulkfood/model_v8.py similarity index 100% rename from 25-class-metaprog/bulkfood/model_v8.py rename to 24-class-metaprog/bulkfood/model_v8.py diff --git a/25-class-metaprog/checked/decorator/checkeddeco.py b/24-class-metaprog/checked/decorator/checkeddeco.py similarity index 100% rename from 25-class-metaprog/checked/decorator/checkeddeco.py rename to 24-class-metaprog/checked/decorator/checkeddeco.py diff --git a/25-class-metaprog/checked/decorator/checkeddeco_demo.py b/24-class-metaprog/checked/decorator/checkeddeco_demo.py similarity index 100% rename from 25-class-metaprog/checked/decorator/checkeddeco_demo.py rename to 24-class-metaprog/checked/decorator/checkeddeco_demo.py diff --git a/25-class-metaprog/checked/decorator/checkeddeco_test.py b/24-class-metaprog/checked/decorator/checkeddeco_test.py similarity index 100% rename from 25-class-metaprog/checked/decorator/checkeddeco_test.py rename to 24-class-metaprog/checked/decorator/checkeddeco_test.py diff --git a/25-class-metaprog/checked/initsub/checked_demo.py b/24-class-metaprog/checked/initsub/checked_demo.py similarity index 100% rename from 25-class-metaprog/checked/initsub/checked_demo.py rename to 24-class-metaprog/checked/initsub/checked_demo.py diff --git a/25-class-metaprog/checked/initsub/checkedlib.py b/24-class-metaprog/checked/initsub/checkedlib.py similarity index 100% rename from 25-class-metaprog/checked/initsub/checkedlib.py rename to 24-class-metaprog/checked/initsub/checkedlib.py diff --git a/25-class-metaprog/checked/initsub/checkedlib_test.py b/24-class-metaprog/checked/initsub/checkedlib_test.py similarity index 100% rename from 25-class-metaprog/checked/initsub/checkedlib_test.py rename to 24-class-metaprog/checked/initsub/checkedlib_test.py diff --git a/25-class-metaprog/checked/metaclass/checked_demo.py b/24-class-metaprog/checked/metaclass/checked_demo.py similarity index 100% rename from 25-class-metaprog/checked/metaclass/checked_demo.py rename to 24-class-metaprog/checked/metaclass/checked_demo.py diff --git a/25-class-metaprog/checked/metaclass/checkedlib.py b/24-class-metaprog/checked/metaclass/checkedlib.py similarity index 100% rename from 25-class-metaprog/checked/metaclass/checkedlib.py rename to 24-class-metaprog/checked/metaclass/checkedlib.py diff --git a/25-class-metaprog/checked/metaclass/checkedlib_test.py b/24-class-metaprog/checked/metaclass/checkedlib_test.py similarity index 100% rename from 25-class-metaprog/checked/metaclass/checkedlib_test.py rename to 24-class-metaprog/checked/metaclass/checkedlib_test.py diff --git a/25-class-metaprog/evaltime/builderlib.py b/24-class-metaprog/evaltime/builderlib.py similarity index 100% rename from 25-class-metaprog/evaltime/builderlib.py rename to 24-class-metaprog/evaltime/builderlib.py diff --git a/25-class-metaprog/evaltime/evaldemo.py b/24-class-metaprog/evaltime/evaldemo.py similarity index 100% rename from 25-class-metaprog/evaltime/evaldemo.py rename to 24-class-metaprog/evaltime/evaldemo.py diff --git a/25-class-metaprog/evaltime/evaldemo_meta.py b/24-class-metaprog/evaltime/evaldemo_meta.py similarity index 100% rename from 25-class-metaprog/evaltime/evaldemo_meta.py rename to 24-class-metaprog/evaltime/evaldemo_meta.py diff --git a/25-class-metaprog/evaltime/metalib.py b/24-class-metaprog/evaltime/metalib.py similarity index 100% rename from 25-class-metaprog/evaltime/metalib.py rename to 24-class-metaprog/evaltime/metalib.py diff --git a/25-class-metaprog/factories.py b/24-class-metaprog/factories.py similarity index 100% rename from 25-class-metaprog/factories.py rename to 24-class-metaprog/factories.py diff --git a/25-class-metaprog/factories_ducktyped.py b/24-class-metaprog/factories_ducktyped.py similarity index 100% rename from 25-class-metaprog/factories_ducktyped.py rename to 24-class-metaprog/factories_ducktyped.py diff --git a/25-class-metaprog/hours/hours.py b/24-class-metaprog/hours/hours.py similarity index 100% rename from 25-class-metaprog/hours/hours.py rename to 24-class-metaprog/hours/hours.py diff --git a/25-class-metaprog/hours/hours_test.py b/24-class-metaprog/hours/hours_test.py similarity index 100% rename from 25-class-metaprog/hours/hours_test.py rename to 24-class-metaprog/hours/hours_test.py diff --git a/25-class-metaprog/metabunch/README.md b/24-class-metaprog/metabunch/README.md similarity index 100% rename from 25-class-metaprog/metabunch/README.md rename to 24-class-metaprog/metabunch/README.md diff --git a/25-class-metaprog/metabunch/from3.6/bunch.py b/24-class-metaprog/metabunch/from3.6/bunch.py similarity index 100% rename from 25-class-metaprog/metabunch/from3.6/bunch.py rename to 24-class-metaprog/metabunch/from3.6/bunch.py diff --git a/25-class-metaprog/metabunch/from3.6/bunch_test.py b/24-class-metaprog/metabunch/from3.6/bunch_test.py similarity index 100% rename from 25-class-metaprog/metabunch/from3.6/bunch_test.py rename to 24-class-metaprog/metabunch/from3.6/bunch_test.py diff --git a/25-class-metaprog/metabunch/nutshell3e/bunch.py b/24-class-metaprog/metabunch/nutshell3e/bunch.py similarity index 100% rename from 25-class-metaprog/metabunch/nutshell3e/bunch.py rename to 24-class-metaprog/metabunch/nutshell3e/bunch.py diff --git a/25-class-metaprog/metabunch/nutshell3e/bunch_test.py b/24-class-metaprog/metabunch/nutshell3e/bunch_test.py similarity index 100% rename from 25-class-metaprog/metabunch/nutshell3e/bunch_test.py rename to 24-class-metaprog/metabunch/nutshell3e/bunch_test.py diff --git a/25-class-metaprog/metabunch/original/bunch.py b/24-class-metaprog/metabunch/original/bunch.py similarity index 100% rename from 25-class-metaprog/metabunch/original/bunch.py rename to 24-class-metaprog/metabunch/original/bunch.py diff --git a/25-class-metaprog/metabunch/original/bunch_test.py b/24-class-metaprog/metabunch/original/bunch_test.py similarity index 100% rename from 25-class-metaprog/metabunch/original/bunch_test.py rename to 24-class-metaprog/metabunch/original/bunch_test.py diff --git a/25-class-metaprog/metabunch/pre3.6/bunch.py b/24-class-metaprog/metabunch/pre3.6/bunch.py similarity index 100% rename from 25-class-metaprog/metabunch/pre3.6/bunch.py rename to 24-class-metaprog/metabunch/pre3.6/bunch.py diff --git a/25-class-metaprog/metabunch/pre3.6/bunch_test.py b/24-class-metaprog/metabunch/pre3.6/bunch_test.py similarity index 100% rename from 25-class-metaprog/metabunch/pre3.6/bunch_test.py rename to 24-class-metaprog/metabunch/pre3.6/bunch_test.py diff --git a/25-class-metaprog/persistent/.gitignore b/24-class-metaprog/persistent/.gitignore similarity index 100% rename from 25-class-metaprog/persistent/.gitignore rename to 24-class-metaprog/persistent/.gitignore diff --git a/25-class-metaprog/persistent/dblib.py b/24-class-metaprog/persistent/dblib.py similarity index 100% rename from 25-class-metaprog/persistent/dblib.py rename to 24-class-metaprog/persistent/dblib.py diff --git a/25-class-metaprog/persistent/dblib_test.py b/24-class-metaprog/persistent/dblib_test.py similarity index 100% rename from 25-class-metaprog/persistent/dblib_test.py rename to 24-class-metaprog/persistent/dblib_test.py diff --git a/25-class-metaprog/persistent/persistlib.py b/24-class-metaprog/persistent/persistlib.py similarity index 100% rename from 25-class-metaprog/persistent/persistlib.py rename to 24-class-metaprog/persistent/persistlib.py diff --git a/25-class-metaprog/persistent/persistlib_test.py b/24-class-metaprog/persistent/persistlib_test.py similarity index 100% rename from 25-class-metaprog/persistent/persistlib_test.py rename to 24-class-metaprog/persistent/persistlib_test.py diff --git a/25-class-metaprog/qualname/fakedjango.py b/24-class-metaprog/qualname/fakedjango.py similarity index 100% rename from 25-class-metaprog/qualname/fakedjango.py rename to 24-class-metaprog/qualname/fakedjango.py diff --git a/25-class-metaprog/qualname/models.py b/24-class-metaprog/qualname/models.py similarity index 100% rename from 25-class-metaprog/qualname/models.py rename to 24-class-metaprog/qualname/models.py diff --git a/25-class-metaprog/sentinel/sentinel.py b/24-class-metaprog/sentinel/sentinel.py similarity index 100% rename from 25-class-metaprog/sentinel/sentinel.py rename to 24-class-metaprog/sentinel/sentinel.py diff --git a/25-class-metaprog/sentinel/sentinel_test.py b/24-class-metaprog/sentinel/sentinel_test.py similarity index 100% rename from 25-class-metaprog/sentinel/sentinel_test.py rename to 24-class-metaprog/sentinel/sentinel_test.py diff --git a/25-class-metaprog/setattr/example_from_leo.py b/24-class-metaprog/setattr/example_from_leo.py similarity index 100% rename from 25-class-metaprog/setattr/example_from_leo.py rename to 24-class-metaprog/setattr/example_from_leo.py diff --git a/25-class-metaprog/slots/slots_timing.py b/24-class-metaprog/slots/slots_timing.py similarity index 100% rename from 25-class-metaprog/slots/slots_timing.py rename to 24-class-metaprog/slots/slots_timing.py diff --git a/25-class-metaprog/tinyenums/microenum.py b/24-class-metaprog/tinyenums/microenum.py similarity index 100% rename from 25-class-metaprog/tinyenums/microenum.py rename to 24-class-metaprog/tinyenums/microenum.py diff --git a/25-class-metaprog/tinyenums/microenum_demo.py b/24-class-metaprog/tinyenums/microenum_demo.py similarity index 100% rename from 25-class-metaprog/tinyenums/microenum_demo.py rename to 24-class-metaprog/tinyenums/microenum_demo.py diff --git a/25-class-metaprog/tinyenums/nanoenum.py b/24-class-metaprog/tinyenums/nanoenum.py similarity index 100% rename from 25-class-metaprog/tinyenums/nanoenum.py rename to 24-class-metaprog/tinyenums/nanoenum.py diff --git a/25-class-metaprog/tinyenums/nanoenum_demo.py b/24-class-metaprog/tinyenums/nanoenum_demo.py similarity index 100% rename from 25-class-metaprog/tinyenums/nanoenum_demo.py rename to 24-class-metaprog/tinyenums/nanoenum_demo.py diff --git a/README.md b/README.md index 102993a..f0854e9 100644 --- a/README.md +++ b/README.md @@ -41,13 +41,12 @@ Part / Chapter #|Title|Directory|1st ed. Chapter # 15|More About Type Hints|[15-more-types](15-more-types)|🆕 16|Operator Overloading: Doing It Right|[16-op-overloading](16-op-overloading)|13 **V – Control Flow**| -17|Iterables, Iterators, and Generators|[17-it-generator](17-it-generator)|14 +17|Iterators, Generators, and Classic Coroutines|[17-it-generator](17-it-generator)|14 18|Context Managers and else Blocks|[18-with-match](18-with-match)|15 -19|Classic Coroutines|[19-coroutine](19-coroutine)|16 -20|Concurrency Models in Python|[20-concurrency](20-concurrency)|🆕 -21|Concurrency with Futures|[21-futures](21-futures)|17 -22|Asynchronous Programming|[22-async](22-async)|18 +19|Concurrency Models in Python|[19-concurrency](19-concurrency)|🆕 +20|Concurrency with Futures|[20-futures](20-futures)|17 +21|Asynchronous Programming|[21-async](21-async)|18 **VI – Metaprogramming**| -23|Dynamic Attributes and Properties|[22-dyn-attr-prop](22-dyn-attr-prop)|19 -24|Attribute Descriptors|[23-descriptor](23-descriptor)|20 -25|Class Metaprogramming|[24-class-metaprog](24-class-metaprog)|21 +22|Dynamic Attributes and Properties|[22-dyn-attr-prop](22-dyn-attr-prop)|19 +23|Attribute Descriptors|[23-descriptor](23-descriptor)|20 +24|Class Metaprogramming|[24-class-metaprog](24-class-metaprog)|21 From 6527037ae7319ba370a1ee2d9fe79214d0ed9452 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 15 Sep 2021 22:48:08 -0300 Subject: [PATCH 067/127] moved lispy from 02 to 18 --- 02-array-seq/lispy/py3.10/examples_test.py | 109 -------- 02-array-seq/lispy/py3.10/meta_test.py | 69 ----- 02-array-seq/lispy/py3.9-no-hints/README.md | 33 --- 02-array-seq/lispy/py3.9-no-hints/lis.py | 142 ---------- 02-array-seq/lispy/py3.9-no-hints/lis_test.py | 166 ----------- 05-data-classes/dataclass/hackerclub.py | 2 +- .../dataclass/hackerclub_annotated.py | 2 +- 17-it-generator/aritprog_v3.py | 6 +- 17-it-generator/coroaverager.py | 43 +++ 17-it-generator/coroaverager2.py | 96 +++++++ 17-it-generator/fibo_gen.py | 7 + 17-it-generator/sentence_gen.py | 2 +- 18-with-match/lisplus/examples_test.py | 109 -------- 18-with-match/lisplus/lis.py | 192 ------------- 18-with-match/lisplus/lis_test.py | 182 ------------ 18-with-match/lisplus/meta_test.py | 64 ----- {02-array-seq => 18-with-match}/lispy/LICENSE | 0 .../lispy/README.md | 0 .../lispy/original/LICENSE | 0 .../lispy/original/README.md | 0 .../lispy/original/lis.py | 0 .../lispy/original/lispy.py | 0 .../lispy/original/lispytest.py | 0 18-with-match/lispy/py3.10/examples_test.py | 259 ++++++++++++++++++ .../lispy/py3.10/lis.py | 178 +++++++----- .../lispy/py3.10/lis_test.py | 0 18-with-match/lispy/py3.10/quicksort.scm | 17 ++ .../lispy/py3.9/README.md | 0 .../lispy/py3.9/lis.py | 18 +- .../lispy/py3.9/lis_test.py | 0 18-with-match/mirror.py | 28 +- 18-with-match/mirror_gen.py | 22 +- 18-with-match/mirror_gen_exc.py | 9 +- 20-futures/getflags/flags2_asyncio.py | 4 +- 20-futures/getflags/flags3_asyncio.py | 4 +- 20-futures/getflags/requirements.txt | 16 +- 20-futures/getflags/slow_server.py | 1 + 22-dyn-attr-prop/oscon/demo_schedule2.py | 25 -- 38 files changed, 588 insertions(+), 1217 deletions(-) delete mode 100644 02-array-seq/lispy/py3.10/examples_test.py delete mode 100644 02-array-seq/lispy/py3.10/meta_test.py delete mode 100644 02-array-seq/lispy/py3.9-no-hints/README.md delete mode 100644 02-array-seq/lispy/py3.9-no-hints/lis.py delete mode 100644 02-array-seq/lispy/py3.9-no-hints/lis_test.py create mode 100644 17-it-generator/coroaverager.py create mode 100644 17-it-generator/coroaverager2.py create mode 100644 17-it-generator/fibo_gen.py delete mode 100644 18-with-match/lisplus/examples_test.py delete mode 100644 18-with-match/lisplus/lis.py delete mode 100644 18-with-match/lisplus/lis_test.py delete mode 100644 18-with-match/lisplus/meta_test.py rename {02-array-seq => 18-with-match}/lispy/LICENSE (100%) rename {02-array-seq => 18-with-match}/lispy/README.md (100%) rename {02-array-seq => 18-with-match}/lispy/original/LICENSE (100%) rename {02-array-seq => 18-with-match}/lispy/original/README.md (100%) rename {02-array-seq => 18-with-match}/lispy/original/lis.py (100%) rename {02-array-seq => 18-with-match}/lispy/original/lispy.py (100%) rename {02-array-seq => 18-with-match}/lispy/original/lispytest.py (100%) create mode 100644 18-with-match/lispy/py3.10/examples_test.py rename {02-array-seq => 18-with-match}/lispy/py3.10/lis.py (70%) mode change 100644 => 100755 rename {02-array-seq => 18-with-match}/lispy/py3.10/lis_test.py (100%) create mode 100644 18-with-match/lispy/py3.10/quicksort.scm rename {02-array-seq => 18-with-match}/lispy/py3.9/README.md (100%) rename {02-array-seq => 18-with-match}/lispy/py3.9/lis.py (91%) rename {02-array-seq => 18-with-match}/lispy/py3.9/lis_test.py (100%) delete mode 100755 22-dyn-attr-prop/oscon/demo_schedule2.py diff --git a/02-array-seq/lispy/py3.10/examples_test.py b/02-array-seq/lispy/py3.10/examples_test.py deleted file mode 100644 index 38510a3..0000000 --- a/02-array-seq/lispy/py3.10/examples_test.py +++ /dev/null @@ -1,109 +0,0 @@ -""" -Doctests for `parse`: - ->>> from lis import parse - -# tag::PARSE_DEMO[] ->>> parse('1.5') # <1> -1.5 ->>> parse('set!') # <2> -'set!' ->>> parse('(gcd 18 44)') # <3> -['gcd', 18, 44] ->>> parse('(- m (* n (// m n)))') # <4> -['-', 'm', ['*', 'n', ['//', 'm', 'n']]] - -# end::PARSE_DEMO[] - -""" - -import math - -from lis import run - - -fact_src = """ -(define (! n) - (if (< n 2) - 1 - (* n (! (- n 1))) - ) -) -(! 42) -""" -def test_factorial(): - got = run(fact_src) - assert got == 1405006117752879898543142606244511569936384000000000 - assert got == math.factorial(42) - - -gcd_src = """ -(define (mod m n) - (- m (* n (// m n)))) -(define (gcd m n) - (if (= n 0) - m - (gcd n (mod m n)))) -(gcd 18 45) -""" -def test_gcd(): - got = run(gcd_src) - assert got == 9 - - -quicksort_src = """ -(define (quicksort lst) - (if (null? lst) - lst - (begin - (define pivot (car lst)) - (define rest (cdr lst)) - (append - (quicksort - (filter (lambda (x) (< x pivot)) rest)) - (list pivot) - (quicksort - (filter (lambda (x) (>= x pivot)) rest))) - ) - ) -) -(quicksort (list 2 1 6 3 4 0 8 9 7 5)) -""" -def test_quicksort(): - got = run(quicksort_src) - assert got == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - - -# Example from Structure and Interpretation of Computer Programs -# https://mitpress.mit.edu/sites/default/files/sicp/full-text/sicp/book/node12.html - -newton_src = """ -(define (sqrt x) - (sqrt-iter 1.0 x)) -(define (sqrt-iter guess x) - (if (good-enough? guess x) - guess - (sqrt-iter (improve guess x) x))) -(define (good-enough? guess x) - (< (abs (- (* guess guess) x)) 0.001)) -(define (improve guess x) - (average guess (/ x guess))) -(define (average x y) - (/ (+ x y) 2)) -(sqrt 123454321) -""" -def test_newton(): - got = run(newton_src) - assert math.isclose(got, 11111) - - -closure_src = """ -(define (make-adder increment) - (lambda (x) (+ increment x)) -) -(define inc (make-adder 1)) -(inc 99) -""" -def test_newton(): - got = run(closure_src) - assert got == 100 diff --git a/02-array-seq/lispy/py3.10/meta_test.py b/02-array-seq/lispy/py3.10/meta_test.py deleted file mode 100644 index 3ddc3f3..0000000 --- a/02-array-seq/lispy/py3.10/meta_test.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -Tests for developing a meta-circular interpreter, step-by-step. -""" - - -import operator as op - -from lis import run - -env_scm = """ -(define standard-env (list - (list (quote +) +) - (list (quote -) -) -)) -standard-env -""" - -def test_env_build(): - got = run(env_scm) - assert got == [['+', op.add], ['-', op.sub]] - -scan_scm = """ -(define l (quote (a b c))) -(define (scan what where) - (if (null? where) - () - (if (eq? what (car where)) - what - (scan what (cdr where)))) -) -""" - -def test_scan(): - source = scan_scm + '(scan (quote a) l )' - got = run(source) - assert got == 'a' - - -def test_scan_not_found(): - source = scan_scm + '(scan (quote z) l )' - got = run(source) - assert got == [] - - -lookup_scm = """ -(define env (list - (list (quote +) +) - (list (quote -) -) -)) -(define (lookup what where) - (if (null? where) - () - (if (eq? what (car (car where))) - (car (cdr (car where))) - (lookup what (cdr where)))) -) -""" - -def test_lookup(): - source = lookup_scm + '(lookup (quote +) env)' - got = run(source) - assert got == op.add - - -def test_lookup_not_found(): - source = lookup_scm + '(lookup (quote z) env )' - got = run(source) - assert got == [] - diff --git a/02-array-seq/lispy/py3.9-no-hints/README.md b/02-array-seq/lispy/py3.9-no-hints/README.md deleted file mode 100644 index 2b6f4ea..0000000 --- a/02-array-seq/lispy/py3.9-no-hints/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Changes from the original - -While adapting Peter Norvig's [lis.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lis.py) for -use in _Fluent Python, Second Edition_, I made a few changes for didactic reasons. - -_Luciano Ramalho_ - -## Significant changes - -* Make the `lambda` form accept more than one expression as the body. This is consistent with _Scheme_ syntax, and provides a useful example for the book. To implement this: - * In `Procedure.__call__`: evaluate `self.body` as a list of expressions, instead of a single expression. Return the value of the last expression. - * In `evaluate()`: when processing `lambda`, unpack expression into `(_, parms, *body)`, to accept a list of expressions as the body. -* Remove the `global_env` global `dict`. It is only used as a default value for the `env` parameter in `evaluate()`, but it is unsafe to use mutable data structures as parameter default values. To implement this: - * In `repl()`: create local variable `global_env` and pass it as the `env` paramater of `evaluate()`. - * In `evaluate()`, remove `global_env` default value for `env`. -* Rewrite the custom test script -[lispytest.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispytest.py) as -[lis_test.py](https://github.com/fluentpython/example-code-2e/blob/master/02-array-seq/lispy/py3.9/lis_test.py): -a standard [pytest](https://docs.pytest.org) test suite including new test cases, preserving all Norvig's the test cases for -[lis.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lis.py) -but removing the test cases for the features implemented only in -[lispy.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispy.py) - - -## Name changes - -Cosmetic changes to make the code look more familiar to -Python programmers, the audience of _Fluent Python_. - -* Rename `eval()` to `evaluate()`, to avoid confusion with Python's `eval` built-in function. -* Refer to the list class as `list` instead of aliasing as `List`, to avoid confusion with `typing.List` which is often imported as `List`. -* Import `collections.ChainMap` as `ChainMap` instead of `Environment`. - diff --git a/02-array-seq/lispy/py3.9-no-hints/lis.py b/02-array-seq/lispy/py3.9-no-hints/lis.py deleted file mode 100644 index c74c3ae..0000000 --- a/02-array-seq/lispy/py3.9-no-hints/lis.py +++ /dev/null @@ -1,142 +0,0 @@ -################ Lispy: Scheme Interpreter in Python 3.9 - -## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html -## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) -## by Luciano Ramalho, adding type hints and pattern matching. - -################ Imports and Types - -import math -import operator as op -from collections import ChainMap -from typing import Any - -Symbol = str # A Lisp Symbol is implemented as a Python str -Number = (int, float) # A Lisp Number is implemented as a Python int or float - -class Procedure: - "A user-defined Scheme procedure." - def __init__(self, parms, body, env): - self.parms, self.body, self.env = parms, body, env - def __call__(self, *args): - env = ChainMap(dict(zip(self.parms, args)), self.env) - for exp in self.body: - result = evaluate(exp, env) - return result - - -################ Global Environment - -def standard_env(): - "An environment with some Scheme standard procedures." - env = {} - env.update(vars(math)) # sin, cos, sqrt, pi, ... - env.update({ - '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, - '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, - 'abs': abs, - 'append': op.add, - 'apply': lambda proc, args: proc(*args), - 'begin': lambda *x: x[-1], - 'car': lambda x: x[0], - 'cdr': lambda x: x[1:], - 'cons': lambda x,y: [x] + y, - 'eq?': op.is_, - 'equal?': op.eq, - 'length': len, - 'list': lambda *x: list(x), - 'list?': lambda x: isinstance(x,list), - 'map': lambda *args: list(map(*args)), - 'max': max, - 'min': min, - 'not': op.not_, - 'null?': lambda x: x == [], - 'number?': lambda x: isinstance(x, Number), - 'procedure?': callable, - 'round': round, - 'symbol?': lambda x: isinstance(x, Symbol), - }) - return env - -################ Parsing: parse, tokenize, and read_from_tokens - -def parse(program): - "Read a Scheme expression from a string." - return read_from_tokens(tokenize(program)) - -def tokenize(s): - "Convert a string into a list of tokens." - return s.replace('(',' ( ').replace(')',' ) ').split() - -def read_from_tokens(tokens): - "Read an expression from a sequence of tokens." - if len(tokens) == 0: - raise SyntaxError('unexpected EOF while reading') - token = tokens.pop(0) - if '(' == token: - exp = [] - while tokens[0] != ')': - exp.append(read_from_tokens(tokens)) - tokens.pop(0) # discard ')' - return exp - elif ')' == token: - raise SyntaxError('unexpected )') - else: - return parse_atom(token) - - -def parse_atom(token: str): - "Numbers become numbers; every other token is a symbol." - try: - return int(token) - except ValueError: - try: - return float(token) - except ValueError: - return Symbol(token) - - -################ Interaction: A REPL - -def repl(prompt: str = 'lis.py> ') -> None: - "A prompt-read-eval-print loop." - global_env = standard_env() - while True: - val = evaluate(parse(input(prompt)), global_env) - if val is not None: - print(lispstr(val)) - - -def lispstr(exp): - "Convert a Python object back into a Lisp-readable string." - if isinstance(exp, list): - return '(' + ' '.join(map(lispstr, exp)) + ')' - else: - return str(exp) - - -################ eval - -def evaluate(x, env): - "Evaluate an expression in an environment." - if isinstance(x, Symbol): # variable reference - return env[x] - elif not isinstance(x, list): # constant literal - return x - elif x[0] == 'quote': # (quote exp) - (_, exp) = x - return exp - elif x[0] == 'if': # (if test conseq alt) - (_, test, conseq, alt) = x - exp = (conseq if evaluate(test, env) else alt) - return evaluate(exp, env) - elif x[0] == 'define': # (define var exp) - (_, var, exp) = x - env[var] = evaluate(exp, env) - elif x[0] == 'lambda': # (lambda (var...) body) - (_, parms, *body) = x - return Procedure(parms, body, env) - else: # (proc arg...) - proc = evaluate(x[0], env) - args = [evaluate(exp, env) for exp in x[1:]] - return proc(*args) diff --git a/02-array-seq/lispy/py3.9-no-hints/lis_test.py b/02-array-seq/lispy/py3.9-no-hints/lis_test.py deleted file mode 100644 index f5f4f8e..0000000 --- a/02-array-seq/lispy/py3.9-no-hints/lis_test.py +++ /dev/null @@ -1,166 +0,0 @@ -from pytest import mark, fixture - -from lis import parse, evaluate, standard_env - -############################################################# tests for parse - -@mark.parametrize( 'source, expected', [ - ('7', 7), - ('x', 'x'), - ('(sum 1 2 3)', ['sum', 1, 2, 3]), - ('(+ (* 2 100) (* 1 10))', ['+', ['*', 2, 100], ['*', 1, 10]]), - ('99 100', 99), # parse stops at the first complete expression - ('(a)(b)', ['a']), -]) -def test_parse(source: str, expected): - got = parse(source) - assert got == expected - - -########################################################## tests for evaluate - -# Norvig's tests are not isolated: they assume the -# same environment from first to last test. -global_env_for_first_test = standard_env() - -@mark.parametrize( 'source, expected', [ - ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), - ("(+ 2 2)", 4), - ("(+ (* 2 100) (* 1 10))", 210), - ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), - ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), - ("(define x 3)", None), - ("x", 3), - ("(+ x x)", 6), - ("((lambda (x) (+ x x)) 5)", 10), - ("(define twice (lambda (x) (* 2 x)))", None), - ("(twice 5)", 10), - ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), - ("((compose list twice) 5)", [10]), - ("(define repeat (lambda (f) (compose f f)))", None), - ("((repeat twice) 5)", 20), - ("((repeat (repeat twice)) 5)", 80), - ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), - ("(fact 3)", 6), - ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), - ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), - ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), - ("""(define combine (lambda (f) - (lambda (x y) - (if (null? x) (quote ()) - (f (list (car x) (car y)) - ((combine f) (cdr x) (cdr y)))))))""", None), - ("(define zip (combine cons))", None), - ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), - ("""(define riff-shuffle (lambda (deck) - (begin - (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) - (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) - (define mid (lambda (seq) (/ (length seq) 2))) - ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), - ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), - ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), - ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), -]) -def test_evaluate(source, expected): - got = evaluate(parse(source), global_env_for_first_test) - assert got == expected - - -@fixture -def std_env(): - return standard_env() - -# tests for each of the cases in evaluate - -def test_evaluate_variable(): - env = dict(x=10) - source = 'x' - expected = 10 - got = evaluate(parse(source), env) - assert got == expected - - -def test_evaluate_literal(std_env): - source = '3.3' - expected = 3.3 - got = evaluate(parse(source), std_env) - assert got == expected - - -def test_evaluate_quote(std_env): - source = '(quote (1.1 is not 1))' - expected = [1.1, 'is', 'not', 1] - got = evaluate(parse(source), std_env) - assert got == expected - - -def test_evaluate_if_true(std_env) -> None: - source = '(if 1 10 no-such-thing)' - expected = 10 - got = evaluate(parse(source), std_env) - assert got == expected - - -def test_evaluate_if_false(std_env) -> None: - source = '(if 0 no-such-thing 20)' - expected = 20 - got = evaluate(parse(source), std_env) - assert got == expected - - -def test_define(std_env) -> None: - source = '(define answer (* 6 7))' - got = evaluate(parse(source), std_env) - assert got is None - assert std_env['answer'] == 42 - - -def test_lambda(std_env) -> None: - source = '(lambda (a b) (if (>= a b) a b))' - func = evaluate(parse(source), std_env) - assert func.parms == ['a', 'b'] - assert func.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] - assert func.env is std_env - assert func(1, 2) == 2 - assert func(3, 2) == 3 - - -def test_begin(std_env) -> None: - source = """ - (begin - (define x (* 2 3)) - (* x 7) - ) - """ - got = evaluate(parse(source), std_env) - assert got == 42 - - -def test_invocation_builtin_car(std_env) -> None: - source = '(car (quote (11 22 33)))' - got = evaluate(parse(source), std_env) - assert got == 11 - - -def test_invocation_builtin_append(std_env) -> None: - source = '(append (quote (a b)) (quote (c d)))' - got = evaluate(parse(source), std_env) - assert got == ['a', 'b', 'c', 'd'] - - -def test_invocation_builtin_map(std_env) -> None: - source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))' - got = evaluate(parse(source), std_env) - assert got == [2, 4, 6] - - -def test_invocation_user_procedure(std_env): - source = """ - (begin - (define max (lambda (a b) (if (>= a b) a b))) - (max 22 11) - ) - """ - got = evaluate(parse(source), std_env) - assert got == 22 diff --git a/05-data-classes/dataclass/hackerclub.py b/05-data-classes/dataclass/hackerclub.py index 762c2cd..60f8234 100644 --- a/05-data-classes/dataclass/hackerclub.py +++ b/05-data-classes/dataclass/hackerclub.py @@ -6,7 +6,7 @@ >>> anna HackerClubMember(name='Anna Ravenscroft', guests=[], handle='AnnaRaven') -If ``handle`` is ommitted, it's set to the first part of the member's name:: +If ``handle`` is omitted, it's set to the first part of the member's name:: >>> leo = HackerClubMember('Leo Rochael') >>> leo diff --git a/05-data-classes/dataclass/hackerclub_annotated.py b/05-data-classes/dataclass/hackerclub_annotated.py index 2394796..0b87c50 100644 --- a/05-data-classes/dataclass/hackerclub_annotated.py +++ b/05-data-classes/dataclass/hackerclub_annotated.py @@ -6,7 +6,7 @@ >>> anna HackerClubMember(name='Anna Ravenscroft', guests=[], handle='AnnaRaven') -If ``handle`` is ommitted, it's set to the first part of the member's name:: +If ``handle`` is omitted, it's set to the first part of the member's name:: >>> leo = HackerClubMember('Leo Rochael') >>> leo diff --git a/17-it-generator/aritprog_v3.py b/17-it-generator/aritprog_v3.py index 3dd8e18..914f5c9 100644 --- a/17-it-generator/aritprog_v3.py +++ b/17-it-generator/aritprog_v3.py @@ -5,7 +5,7 @@ def aritprog_gen(begin, step, end=None): first = type(begin + step)(begin) ap_gen = itertools.count(first, step) - if end is not None: - ap_gen = itertools.takewhile(lambda n: n < end, ap_gen) - return ap_gen + if end is None: + return ap_gen + return itertools.takewhile(lambda n: n < end, ap_gen) # end::ARITPROG_ITERTOOLS[] diff --git a/17-it-generator/coroaverager.py b/17-it-generator/coroaverager.py new file mode 100644 index 0000000..d976760 --- /dev/null +++ b/17-it-generator/coroaverager.py @@ -0,0 +1,43 @@ +""" +A coroutine to compute a running average + +# tag::CORO_AVERAGER_TEST[] + >>> coro_avg = averager() # <1> + >>> next(coro_avg) # <2> + 0.0 + >>> coro_avg.send(10) # <3> + 10.0 + >>> coro_avg.send(30) + 20.0 + >>> coro_avg.send(5) + 15.0 + +# end::CORO_AVERAGER_TEST[] +# tag::CORO_AVERAGER_TEST_CONT[] + + >>> coro_avg.send(20) # <1> + 16.25 + >>> coro_avg.close() # <2> + >>> coro_avg.close() # <3> + >>> coro_avg.send(5) # <4> + Traceback (most recent call last): + ... + StopIteration + +# end::CORO_AVERAGER_TEST_CONT[] + +""" + +# tag::CORO_AVERAGER[] +from collections.abc import Generator + +def averager() -> Generator[float, float, None]: # <1> + total = 0.0 + count = 0 + average = 0.0 + while True: # <2> + term = yield average # <3> + total += term + count += 1 + average = total/count +# end::CORO_AVERAGER[] diff --git a/17-it-generator/coroaverager2.py b/17-it-generator/coroaverager2.py new file mode 100644 index 0000000..3a15f5e --- /dev/null +++ b/17-it-generator/coroaverager2.py @@ -0,0 +1,96 @@ +""" +A coroutine to compute a running average. + +Testing ``averager2`` by itself:: + +# tag::RETURNING_AVERAGER_DEMO_1[] + + >>> coro_avg = averager2() + >>> next(coro_avg) + >>> coro_avg.send(10) # <1> + >>> coro_avg.send(30) + >>> coro_avg.send(6.5) + >>> coro_avg.close() # <2> + +# end::RETURNING_AVERAGER_DEMO_1[] + +Catching `StopIteration` to extract the value returned by +the coroutine:: + +# tag::RETURNING_AVERAGER_DEMO_2[] + + >>> coro_avg = averager2() + >>> next(coro_avg) + >>> coro_avg.send(10) + >>> coro_avg.send(30) + >>> coro_avg.send(6.5) + >>> try: + ... coro_avg.send(STOP) # <1> + ... except StopIteration as exc: + ... result = exc.value # <2> + ... + >>> result # <3> + Result(count=3, average=15.5) + +# end::RETURNING_AVERAGER_DEMO_2[] + +Using `yield from`: + + +# tag::RETURNING_AVERAGER_DEMO_3[] + + >>> def compute(): + ... res = yield from averager2(True) # <1> + ... print('computed:', res) # <2> + ... return res # <3> + ... + >>> comp = compute() # <4> + >>> for v in [None, 10, 20, 30, STOP]: # <5> + ... try: + ... comp.send(v) # <6> + ... except StopIteration as exc: # <7> + ... result = exc.value + received: 10 + received: 20 + received: 30 + received: + computed: Result(count=3, average=20.0) + >>> result # <8> + Result(count=3, average=20.0) + +# end::RETURNING_AVERAGER_DEMO_3[] +""" + +# tag::RETURNING_AVERAGER_TOP[] +from collections.abc import Generator +from typing import Union, NamedTuple + +class Result(NamedTuple): # <1> + count: int # type: ignore # <2> + average: float + +class Sentinel: # <3> + def __repr__(self): + return f'' + +STOP = Sentinel() # <4> + +SendType = Union[float, Sentinel] # <5> +# end::RETURNING_AVERAGER_TOP[] +# tag::RETURNING_AVERAGER[] +def averager2(verbose: bool = False) -> Generator[None, SendType, Result]: # <1> + total = 0.0 + count = 0 + average = 0.0 + while True: + term = yield # <2> + if verbose: + print('received:', term) + if isinstance(term, Sentinel): # <3> + break + total += term # <4> + count += 1 + average = total / count + return Result(count, average) # <5> + +# end::RETURNING_AVERAGER[] diff --git a/17-it-generator/fibo_gen.py b/17-it-generator/fibo_gen.py new file mode 100644 index 0000000..d7296ac --- /dev/null +++ b/17-it-generator/fibo_gen.py @@ -0,0 +1,7 @@ +from collections.abc import Iterator + +def fibonacci() -> Iterator[int]: + a, b = 0, 1 + while True: + yield a + a, b = b, a + b diff --git a/17-it-generator/sentence_gen.py b/17-it-generator/sentence_gen.py index 3dbc606..1f7ebeb 100644 --- a/17-it-generator/sentence_gen.py +++ b/17-it-generator/sentence_gen.py @@ -21,7 +21,7 @@ def __repr__(self): def __iter__(self): for word in self.words: # <1> yield word # <2> - return # <3> + # <3> # done! <4> diff --git a/18-with-match/lisplus/examples_test.py b/18-with-match/lisplus/examples_test.py deleted file mode 100644 index 38510a3..0000000 --- a/18-with-match/lisplus/examples_test.py +++ /dev/null @@ -1,109 +0,0 @@ -""" -Doctests for `parse`: - ->>> from lis import parse - -# tag::PARSE_DEMO[] ->>> parse('1.5') # <1> -1.5 ->>> parse('set!') # <2> -'set!' ->>> parse('(gcd 18 44)') # <3> -['gcd', 18, 44] ->>> parse('(- m (* n (// m n)))') # <4> -['-', 'm', ['*', 'n', ['//', 'm', 'n']]] - -# end::PARSE_DEMO[] - -""" - -import math - -from lis import run - - -fact_src = """ -(define (! n) - (if (< n 2) - 1 - (* n (! (- n 1))) - ) -) -(! 42) -""" -def test_factorial(): - got = run(fact_src) - assert got == 1405006117752879898543142606244511569936384000000000 - assert got == math.factorial(42) - - -gcd_src = """ -(define (mod m n) - (- m (* n (// m n)))) -(define (gcd m n) - (if (= n 0) - m - (gcd n (mod m n)))) -(gcd 18 45) -""" -def test_gcd(): - got = run(gcd_src) - assert got == 9 - - -quicksort_src = """ -(define (quicksort lst) - (if (null? lst) - lst - (begin - (define pivot (car lst)) - (define rest (cdr lst)) - (append - (quicksort - (filter (lambda (x) (< x pivot)) rest)) - (list pivot) - (quicksort - (filter (lambda (x) (>= x pivot)) rest))) - ) - ) -) -(quicksort (list 2 1 6 3 4 0 8 9 7 5)) -""" -def test_quicksort(): - got = run(quicksort_src) - assert got == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - - -# Example from Structure and Interpretation of Computer Programs -# https://mitpress.mit.edu/sites/default/files/sicp/full-text/sicp/book/node12.html - -newton_src = """ -(define (sqrt x) - (sqrt-iter 1.0 x)) -(define (sqrt-iter guess x) - (if (good-enough? guess x) - guess - (sqrt-iter (improve guess x) x))) -(define (good-enough? guess x) - (< (abs (- (* guess guess) x)) 0.001)) -(define (improve guess x) - (average guess (/ x guess))) -(define (average x y) - (/ (+ x y) 2)) -(sqrt 123454321) -""" -def test_newton(): - got = run(newton_src) - assert math.isclose(got, 11111) - - -closure_src = """ -(define (make-adder increment) - (lambda (x) (+ increment x)) -) -(define inc (make-adder 1)) -(inc 99) -""" -def test_newton(): - got = run(closure_src) - assert got == 100 diff --git a/18-with-match/lisplus/lis.py b/18-with-match/lisplus/lis.py deleted file mode 100644 index 7ae0a9d..0000000 --- a/18-with-match/lisplus/lis.py +++ /dev/null @@ -1,192 +0,0 @@ -################ Lispy: Scheme Interpreter in Python 3.10 - -## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html -## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) -## by Luciano Ramalho, adding type hints and pattern matching. - -################ imports and types - -import math -import operator as op -from collections import ChainMap -from collections.abc import MutableMapping, Iterator -from itertools import chain -from typing import Any, TypeAlias - -Symbol: TypeAlias = str -Atom: TypeAlias = float | int | Symbol -Expression: TypeAlias = Atom | list - -Environment: TypeAlias = MutableMapping[Symbol, object] - - -class Procedure: - "A user-defined Scheme procedure." - - def __init__(self, parms: list[Symbol], body: list[Expression], env: Environment): - self.parms = parms - self.body = body - self.env = env - - def __call__(self, *args: Expression) -> Any: - local_env = dict(zip(self.parms, args)) - env: Environment = ChainMap(local_env, self.env) - for exp in self.body: - result = evaluate(exp, env) - return result - - -################ global environment - - -def standard_env() -> Environment: - "An environment with some Scheme standard procedures." - env: Environment = {} - env.update(vars(math)) # sin, cos, sqrt, pi, ... - env.update({ - '+': op.add, - '-': op.sub, - '*': op.mul, - '/': op.truediv, - '//': op.floordiv, - '>': op.gt, - '<': op.lt, - '>=': op.ge, - '<=': op.le, - '=': op.eq, - 'abs': abs, - 'append': lambda *args: list(chain(*args)), - 'apply': lambda proc, args: proc(*args), - 'begin': lambda *x: x[-1], - 'car': lambda x: x[0], - 'cdr': lambda x: x[1:], - 'cons': lambda x, y: [x] + y, - 'eq?': op.is_, - 'equal?': op.eq, - 'filter': lambda *args: list(filter(*args)), - 'length': len, - 'list': lambda *x: list(x), - 'list?': lambda x: isinstance(x, list), - 'map': lambda *args: list(map(*args)), - 'max': max, - 'min': min, - 'not': op.not_, - 'null?': lambda x: x == [], - 'number?': lambda x: isinstance(x, (int, float)), - 'procedure?': callable, - 'round': round, - 'symbol?': lambda x: isinstance(x, Symbol), - }) - return env - - -################ parse, tokenize, and read_from_tokens - - -def parse(program: str) -> Expression: - "Read a Scheme expression from a string." - return read_from_tokens(tokenize(program)) - - -def tokenize(s: str) -> list[str]: - "Convert a string into a list of tokens." - return s.replace('(', ' ( ').replace(')', ' ) ').split() - - -def read_from_tokens(tokens: list[str]) -> Expression: - "Read an expression from a sequence of tokens." - if len(tokens) == 0: - raise SyntaxError('unexpected EOF while reading') - token = tokens.pop(0) - if '(' == token: - exp = [] - while tokens[0] != ')': - exp.append(read_from_tokens(tokens)) - tokens.pop(0) # discard ')' - return exp - elif ')' == token: - raise SyntaxError('unexpected )') - else: - return parse_atom(token) - - -def parse_atom(token: str) -> Atom: - "Numbers become numbers; every other token is a symbol." - try: - return int(token) - except ValueError: - try: - return float(token) - except ValueError: - return Symbol(token) - - -################ interaction: a REPL - - -def repl(prompt: str = 'lis.py> ') -> None: - "A prompt-read-evaluate-print loop." - global_env: Environment = standard_env() - while True: - val = evaluate(parse(input(prompt)), global_env) - if val is not None: - print(lispstr(val)) - - -def lispstr(exp: object) -> str: - "Convert a Python object back into a Lisp-readable string." - if isinstance(exp, list): - return '(' + ' '.join(map(lispstr, exp)) + ')' - else: - return str(exp) - - -################ eval - -# tag::EVALUATE[] -def evaluate(exp: Expression, env: Environment) -> Any: - "Evaluate an expression in an environment." - match exp: - case int(x) | float(x): - return x - case Symbol(var): - return env[var] - case []: - return [] - case ['quote', exp]: - return exp - case ['if', test, consequence, alternative]: - if evaluate(test, env): - return evaluate(consequence, env) - else: - return evaluate(alternative, env) - case ['define', Symbol(var), value_exp]: - env[var] = evaluate(value_exp, env) - case ['define', [Symbol(name), *parms], *body]: - env[name] = Procedure(parms, body, env) - case ['lambda', [*parms], *body]: - return Procedure(parms, body, env) - case [op, *args]: - proc = evaluate(op, env) - values = [evaluate(arg, env) for arg in args] - return proc(*values) - case _: - raise SyntaxError(repr(exp)) -# end::EVALUATE[] - - -################ non-interactive execution - - -def run_lines(source: str) -> Iterator[Any]: - global_env: Environment = standard_env() - tokens = tokenize(source) - while tokens: - exp = read_from_tokens(tokens) - yield evaluate(exp, global_env) - - -def run(source: str) -> Any: - for result in run_lines(source): - pass - return result diff --git a/18-with-match/lisplus/lis_test.py b/18-with-match/lisplus/lis_test.py deleted file mode 100644 index 3688888..0000000 --- a/18-with-match/lisplus/lis_test.py +++ /dev/null @@ -1,182 +0,0 @@ -from typing import Optional - -from pytest import mark, fixture - -from lis import parse, evaluate, Expression, Environment, standard_env - -############################################################# tests for parse - -@mark.parametrize( 'source, expected', [ - ('7', 7), - ('x', 'x'), - ('(sum 1 2 3)', ['sum', 1, 2, 3]), - ('(+ (* 2 100) (* 1 10))', ['+', ['*', 2, 100], ['*', 1, 10]]), - ('99 100', 99), # parse stops at the first complete expression - ('(a)(b)', ['a']), -]) -def test_parse(source: str, expected: Expression) -> None: - got = parse(source) - assert got == expected - - -########################################################## tests for evaluate - -# Norvig's tests are not isolated: they assume the -# same environment from first to last test. -global_env_for_first_test = standard_env() - -@mark.parametrize( 'source, expected', [ - ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), - ("(+ 2 2)", 4), - ("(+ (* 2 100) (* 1 10))", 210), - ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), - ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), - ("(define x 3)", None), - ("x", 3), - ("(+ x x)", 6), - ("((lambda (x) (+ x x)) 5)", 10), - ("(define twice (lambda (x) (* 2 x)))", None), - ("(twice 5)", 10), - ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), - ("((compose list twice) 5)", [10]), - ("(define repeat (lambda (f) (compose f f)))", None), - ("((repeat twice) 5)", 20), - ("((repeat (repeat twice)) 5)", 80), - ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), - ("(fact 3)", 6), - ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), - ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), - ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), - ("""(define combine (lambda (f) - (lambda (x y) - (if (null? x) (quote ()) - (f (list (car x) (car y)) - ((combine f) (cdr x) (cdr y)))))))""", None), - ("(define zip (combine cons))", None), - ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), - ("""(define riff-shuffle (lambda (deck) - (begin - (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) - (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) - (define mid (lambda (seq) (/ (length seq) 2))) - ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), - ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), - ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), - ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), -]) -def test_evaluate(source: str, expected: Optional[Expression]) -> None: - got = evaluate(parse(source), global_env_for_first_test) - assert got == expected - - -@fixture -def std_env() -> Environment: - return standard_env() - -# tests for each of the cases in evaluate - -def test_evaluate_variable() -> None: - env: Environment = dict(x=10) - source = 'x' - expected = 10 - got = evaluate(parse(source), env) - assert got == expected - - -def test_evaluate_literal(std_env: Environment) -> None: - source = '3.3' - expected = 3.3 - got = evaluate(parse(source), std_env) - assert got == expected - - -def test_evaluate_quote(std_env: Environment) -> None: - source = '(quote (1.1 is not 1))' - expected = [1.1, 'is', 'not', 1] - got = evaluate(parse(source), std_env) - assert got == expected - - -def test_evaluate_if_true(std_env: Environment) -> None: - source = '(if 1 10 no-such-thing)' - expected = 10 - got = evaluate(parse(source), std_env) - assert got == expected - - -def test_evaluate_if_false(std_env: Environment) -> None: - source = '(if 0 no-such-thing 20)' - expected = 20 - got = evaluate(parse(source), std_env) - assert got == expected - - -def test_define(std_env: Environment) -> None: - source = '(define answer (* 6 7))' - got = evaluate(parse(source), std_env) - assert got is None - assert std_env['answer'] == 42 - - -def test_lambda(std_env: Environment) -> None: - source = '(lambda (a b) (if (>= a b) a b))' - func = evaluate(parse(source), std_env) - assert func.parms == ['a', 'b'] - assert func.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] - assert func.env is std_env - assert func(1, 2) == 2 - assert func(3, 2) == 3 - - -def test_begin(std_env: Environment) -> None: - source = """ - (begin - (define x (* 2 3)) - (* x 7) - ) - """ - got = evaluate(parse(source), std_env) - assert got == 42 - - -def test_invocation_builtin_car(std_env: Environment) -> None: - source = '(car (quote (11 22 33)))' - got = evaluate(parse(source), std_env) - assert got == 11 - - -def test_invocation_builtin_append(std_env: Environment) -> None: - source = '(append (quote (a b)) (quote (c d)))' - got = evaluate(parse(source), std_env) - assert got == ['a', 'b', 'c', 'd'] - - -def test_invocation_builtin_map(std_env: Environment) -> None: - source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))' - got = evaluate(parse(source), std_env) - assert got == [2, 4, 6] - - -def test_invocation_user_procedure(std_env: Environment) -> None: - source = """ - (begin - (define max (lambda (a b) (if (>= a b) a b))) - (max 22 11) - ) - """ - got = evaluate(parse(source), std_env) - assert got == 22 - - -###################################### for py3.10/lis.py only - -def test_define_function(std_env: Environment) -> None: - source = '(define (max a b) (if (>= a b) a b))' - got = evaluate(parse(source), std_env) - assert got is None - max_fn = std_env['max'] - assert max_fn.parms == ['a', 'b'] - assert max_fn.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] - assert max_fn.env is std_env - assert max_fn(1, 2) == 2 - assert max_fn(3, 2) == 3 diff --git a/18-with-match/lisplus/meta_test.py b/18-with-match/lisplus/meta_test.py deleted file mode 100644 index cb3d062..0000000 --- a/18-with-match/lisplus/meta_test.py +++ /dev/null @@ -1,64 +0,0 @@ -import operator as op - -from lis import run - -env_scm = """ -(define standard-env (list - (list (quote +) +) - (list (quote -) -) -)) -standard-env -""" - -def test_env_build(): - got = run(env_scm) - assert got == [['+', op.add], ['-', op.sub]] - -scan_scm = """ -(define l (quote (a b c))) -(define (scan what where) - (if (null? where) - () - (if (eq? what (car where)) - what - (scan what (cdr where)))) -) -""" - -def test_scan(): - source = scan_scm + '(scan (quote a) l )' - got = run(source) - assert got == 'a' - - -def test_scan_not_found(): - source = scan_scm + '(scan (quote z) l )' - got = run(source) - assert got == [] - - -lookup_scm = """ -(define env (list - (list (quote +) +) - (list (quote -) -) -)) -(define (lookup what where) - (if (null? where) - () - (if (eq? what (car (car where))) - (car (cdr (car where))) - (lookup what (cdr where)))) -) -""" - -def test_lookup(): - source = lookup_scm + '(lookup (quote +) env)' - got = run(source) - assert got == op.add - - -def test_lookup_not_found(): - source = lookup_scm + '(lookup (quote z) env )' - got = run(source) - assert got == [] - diff --git a/02-array-seq/lispy/LICENSE b/18-with-match/lispy/LICENSE similarity index 100% rename from 02-array-seq/lispy/LICENSE rename to 18-with-match/lispy/LICENSE diff --git a/02-array-seq/lispy/README.md b/18-with-match/lispy/README.md similarity index 100% rename from 02-array-seq/lispy/README.md rename to 18-with-match/lispy/README.md diff --git a/02-array-seq/lispy/original/LICENSE b/18-with-match/lispy/original/LICENSE similarity index 100% rename from 02-array-seq/lispy/original/LICENSE rename to 18-with-match/lispy/original/LICENSE diff --git a/02-array-seq/lispy/original/README.md b/18-with-match/lispy/original/README.md similarity index 100% rename from 02-array-seq/lispy/original/README.md rename to 18-with-match/lispy/original/README.md diff --git a/02-array-seq/lispy/original/lis.py b/18-with-match/lispy/original/lis.py similarity index 100% rename from 02-array-seq/lispy/original/lis.py rename to 18-with-match/lispy/original/lis.py diff --git a/02-array-seq/lispy/original/lispy.py b/18-with-match/lispy/original/lispy.py similarity index 100% rename from 02-array-seq/lispy/original/lispy.py rename to 18-with-match/lispy/original/lispy.py diff --git a/02-array-seq/lispy/original/lispytest.py b/18-with-match/lispy/original/lispytest.py similarity index 100% rename from 02-array-seq/lispy/original/lispytest.py rename to 18-with-match/lispy/original/lispytest.py diff --git a/18-with-match/lispy/py3.10/examples_test.py b/18-with-match/lispy/py3.10/examples_test.py new file mode 100644 index 0000000..ed4b68a --- /dev/null +++ b/18-with-match/lispy/py3.10/examples_test.py @@ -0,0 +1,259 @@ +""" +Doctests for `parse` +-------------------- + +# tag::PARSE_ATOM[] +>>> from lis import parse +>>> parse('1.5') +1.5 +>>> parse('ni!') +'ni!' + +# end::PARSE_ATOM[] + +# tag::PARSE_LIST[] +>>> parse('(gcd 18 45)') +['gcd', 18, 45] +>>> parse(''' +... (define double +... (lambda (n) +... (* n 2))) +... ''') +['define', 'double', ['lambda', ['n'], ['*', 'n', 2]]] + +# end::PARSE_LIST[] + +Doctest for `Environment` +------------------------- + +# tag::ENVIRONMENT[] +>>> from lis import Environment +>>> outer_env = {'a': 0, 'b': 1} +>>> inner_env = {'a': 2} +>>> env = Environment(inner_env, outer_env) +>>> env['a'] = 111 # <1> +>>> env['c'] = 222 +>>> env +Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 1}) +>>> env.change('b', 333) # <2> +>>> env +Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 333}) + +# end::ENVIRONMENT[] + +Doctests for `evaluate` +----------------------- + +# tag::EVAL_NUMBER[] +>>> from lis import parse, evaluate, standard_env +>>> evaluate(parse('1.5'), {}) +1.5 + +# end::EVAL_NUMBER[] + +# tag::EVAL_SYMBOL[] +>>> from lis import standard_env +>>> evaluate(parse('+'), standard_env()) + +>>> evaluate(parse('ni!'), standard_env()) +Traceback (most recent call last): + ... +KeyError: 'ni!' + +# end::EVAL_SYMBOL[] + + +# tag::EVAL_QUOTE[] +>>> evaluate(parse('(quote no-such-name)'), {}) +'no-such-name' +>>> evaluate(parse('(quote (99 bottles of beer))'), {}) +[99, 'bottles', 'of', 'beer'] +>>> evaluate(parse('(quote (/ 10 0))'), {}) +['/', 10, 0] + +# end::EVAL_QUOTE[] + +# tag::EVAL_IF[] +>>> evaluate(parse('(if (= 3 3) 1 0))'), standard_env()) +1 +>>> evaluate(parse('(if (= 3 4) 1 0))'), standard_env()) +0 + +# end::EVAL_IF[] + + +# tag::EVAL_LAMBDA[] +>>> expr = '(lambda (a b) (* (/ a b) 100))' +>>> f = evaluate(parse(expr), standard_env()) +>>> f # doctest: +ELLIPSIS + +>>> f(15, 20) +75.0 + +# end::EVAL_LAMBDA[] + +# tag::EVAL_DEFINE[] +>>> global_env = standard_env() +>>> evaluate(parse('(define answer (* 7 6))'), global_env) +>>> global_env['answer'] +42 + +# end::EVAL_DEFINE[] + +# tag::EVAL_DEFUN[] +>>> global_env = standard_env() +>>> percent = '(define (% a b) (* (/ a b) 100))' +>>> evaluate(parse(percent), global_env) +>>> global_env['%'] # doctest: +ELLIPSIS + +>>> global_env['%'](170, 200) +85.0 + +# end::EVAL_DEFUN[] + +function call: + +# tag::EVAL_CALL[] +>>> evaluate(parse('(% (* 12 14) (- 500 100))'), global_env) +42.0 + +# end::EVAL_CALL[] + +# tag::EVAL_SYNTAX_ERROR[] +>>> evaluate(parse('(lambda is not like this)'), standard_env()) +Traceback (most recent call last): + ... +SyntaxError: (lambda is not like this) + +# end::EVAL_SYNTAX_ERROR[] + +""" + +import math + +from lis import run + + +fact_src = """ +(define (! n) + (if (< n 2) + 1 + (* n (! (- n 1))) + ) +) +(! 42) +""" +def test_factorial(): + got = run(fact_src) + assert got == 1405006117752879898543142606244511569936384000000000 + assert got == math.factorial(42) + + +gcd_src = """ +(define (mod m n) + (- m (* n (// m n)))) +(define (gcd m n) + (if (= n 0) + m + (gcd n (mod m n)))) +(gcd 18 45) +""" +def test_gcd(): + got = run(gcd_src) + assert got == 9 + + +quicksort_src = """ +(define (quicksort lst) + (if (null? lst) + lst + (begin + (define pivot (car lst)) + (define rest (cdr lst)) + (append + (quicksort + (filter (lambda (x) (< x pivot)) rest)) + (list pivot) + (quicksort + (filter (lambda (x) (>= x pivot)) rest))) + ) + ) +) +(quicksort (list 2 1 6 3 4 0 8 9 7 5)) +""" +def test_quicksort(): + got = run(quicksort_src) + assert got == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + +# Example from Structure and Interpretation of Computer Programs +# https://mitpress.mit.edu/sites/default/files/sicp/full-text/sicp/book/node12.html + +newton_src = """ +(define (sqrt x) + (sqrt-iter 1.0 x)) +(define (sqrt-iter guess x) + (if (good-enough? guess x) + guess + (sqrt-iter (improve guess x) x))) +(define (good-enough? guess x) + (< (abs (- (* guess guess) x)) 0.001)) +(define (improve guess x) + (average guess (/ x guess))) +(define (average x y) + (/ (+ x y) 2)) +(sqrt 123454321) +""" +def test_newton(): + got = run(newton_src) + assert math.isclose(got, 11111) + + +closure_src = """ +(define (make-adder increment) + (lambda (x) (+ increment x)) +) +(define inc (make-adder 1)) +(inc 99) +""" +def test_newton(): + got = run(closure_src) + assert got == 100 + +closure_with_change_src = """ +(define (make-counter) + (define n 0) + (lambda () + (set! n (+ n 1)) + n) +) +(define counter (make-counter)) +(counter) +(counter) +(counter) +""" +def test_closure_with_change(): + got = run(closure_with_change_src) + assert got == 3 + + +# tag::RUN_AVERAGER[] +closure_averager_src = """ +(define (make-averager) + (define count 0) + (define total 0) + (lambda (new-value) + (set! count (+ count 1)) + (set! total (+ total new-value)) + (/ total count) + ) +) +(define avg (make-averager)) +(avg 10) +(avg 11) +(avg 15) +""" +def test_closure_averager(): + got = run(closure_averager_src) + assert got == 12.0 +# end::RUN_AVERAGER[] \ No newline at end of file diff --git a/02-array-seq/lispy/py3.10/lis.py b/18-with-match/lispy/py3.10/lis.py old mode 100644 new mode 100755 similarity index 70% rename from 02-array-seq/lispy/py3.10/lis.py rename to 18-with-match/lispy/py3.10/lis.py index 035481f..4a5195c --- a/02-array-seq/lispy/py3.10/lis.py +++ b/18-with-match/lispy/py3.10/lis.py @@ -1,46 +1,82 @@ +#!/usr/bin/env python + ################ Lispy: Scheme Interpreter in Python 3.10 ## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html ## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) ## by Luciano Ramalho, adding type hints and pattern matching. + ################ Imports and Types +# tag::IMPORTS[] import math import operator as op from collections import ChainMap -from collections.abc import MutableMapping, Iterator from itertools import chain -from typing import Any, TypeAlias +from typing import Any, TypeAlias, NoReturn Symbol: TypeAlias = str Atom: TypeAlias = float | int | Symbol Expression: TypeAlias = Atom | list +# end::IMPORTS[] -Environment: TypeAlias = MutableMapping[Symbol, object] +################ Parsing: parse, tokenize, and read_from_tokens -class Procedure: - "A user-defined Scheme procedure." +def parse(program: str) -> Expression: + "Read a Scheme expression from a string." + return read_from_tokens(tokenize(program)) - def __init__(self, parms: list[Symbol], body: list[Expression], env: Environment): - self.parms = parms - self.body = body - self.env = env +def tokenize(s: str) -> list[str]: + "Convert a string into a list of tokens." + return s.replace('(', ' ( ').replace(')', ' ) ').split() - def __call__(self, *args: Expression) -> Any: - local_env = dict(zip(self.parms, args)) - env: Environment = ChainMap(local_env, self.env) - for exp in self.body: - result = evaluate(exp, env) - return result +def read_from_tokens(tokens: list[str]) -> Expression: + "Read an expression from a sequence of tokens." + if len(tokens) == 0: + raise SyntaxError('unexpected EOF while reading') + token = tokens.pop(0) + if '(' == token: + exp = [] + while tokens[0] != ')': + exp.append(read_from_tokens(tokens)) + tokens.pop(0) # discard ')' + return exp + elif ')' == token: + raise SyntaxError('unexpected )') + else: + return parse_atom(token) +def parse_atom(token: str) -> Atom: + "Numbers become numbers; every other token is a symbol." + try: + return int(token) + except ValueError: + try: + return float(token) + except ValueError: + return Symbol(token) ################ Global Environment +# tag::ENV_CLASS[] +class Environment(ChainMap): + "A ChainMap that allows changing an item in-place." + + def change(self, key: Symbol, value: object) -> None: + "Find where key is defined and change the value there." + for map in self.maps: + if key in map: + map[key] = value + return + raise KeyError(key) +# end::ENV_CLASS[] + + def standard_env() -> Environment: "An environment with some Scheme standard procedures." - env: Environment = {} + env = Environment() env.update(vars(math)) # sin, cos, sqrt, pi, ... env.update({ '+': op.add, @@ -60,6 +96,7 @@ def standard_env() -> Environment: 'car': lambda x: x[0], 'cdr': lambda x: x[1:], 'cons': lambda x, y: [x] + y, + 'display': lambda x: print(lispstr(x)), 'eq?': op.is_, 'equal?': op.eq, 'filter': lambda *args: list(filter(*args)), @@ -78,68 +115,33 @@ def standard_env() -> Environment: }) return env -################ Parsing: parse, tokenize, and read_from_tokens - -def parse(program: str) -> Expression: - "Read a Scheme expression from a string." - return read_from_tokens(tokenize(program)) - - -def tokenize(s: str) -> list[str]: - "Convert a string into a list of tokens." - return s.replace('(', ' ( ').replace(')', ' ) ').split() - - -def read_from_tokens(tokens: list[str]) -> Expression: - "Read an expression from a sequence of tokens." - if len(tokens) == 0: - raise SyntaxError('unexpected EOF while reading') - token = tokens.pop(0) - if '(' == token: - exp = [] - while tokens[0] != ')': - exp.append(read_from_tokens(tokens)) - tokens.pop(0) # discard ')' - return exp - elif ')' == token: - raise SyntaxError('unexpected )') - else: - return parse_atom(token) - - -def parse_atom(token: str) -> Atom: - "Numbers become numbers; every other token is a symbol." - try: - return int(token) - except ValueError: - try: - return float(token) - except ValueError: - return Symbol(token) - ################ Interaction: A REPL -def repl(prompt: str = 'lis.py> ') -> None: - "A prompt-read-evaluate-print loop." +# tag::REPL[] +def repl() -> NoReturn: + "A prompt-read-eval-print loop." global_env = standard_env() while True: - val = evaluate(parse(input(prompt)), global_env) + ast = parse(input('lis.py> ')) + val = evaluate(ast, global_env) if val is not None: print(lispstr(val)) - def lispstr(exp: object) -> str: "Convert a Python object back into a Lisp-readable string." if isinstance(exp, list): return '(' + ' '.join(map(lispstr, exp)) + ')' else: return str(exp) +# end::REPL[] -################ eval +################ Evaluator # tag::EVALUATE[] +KEYWORDS = ['quote', 'if', 'lambda', 'define', 'set!'] + def evaluate(exp: Expression, env: Environment) -> Any: "Evaluate an expression in an environment." match exp: @@ -149,40 +151,66 @@ def evaluate(exp: Expression, env: Environment) -> Any: return env[var] case []: return [] - case ['quote', exp]: - return exp + case ['quote', x]: + return x case ['if', test, consequence, alternative]: if evaluate(test, env): return evaluate(consequence, env) else: return evaluate(alternative, env) + case ['lambda', [*parms], *body] if body: + return Procedure(parms, body, env) case ['define', Symbol(var), value_exp]: env[var] = evaluate(value_exp, env) - case ['define', [Symbol(name), *parms], *body]: + case ['define', [Symbol(name), *parms], *body] if body: env[name] = Procedure(parms, body, env) - case ['lambda', [*parms], *body]: - return Procedure(parms, body, env) - case [op, *args]: + case ['set!', Symbol(var), value_exp]: + env.change(var, evaluate(value_exp, env)) + case [op, *args] if op not in KEYWORDS: proc = evaluate(op, env) values = [evaluate(arg, env) for arg in args] return proc(*values) case _: - raise SyntaxError(repr(exp)) + raise SyntaxError(lispstr(exp)) # end::EVALUATE[] +# tag::PROCEDURE[] +class Procedure: + "A user-defined Scheme procedure." + + def __init__( # <1> + self, parms: list[Symbol], body: list[Expression], env: Environment + ): + self.parms = parms # <2> + self.body = body + self.env = env + + def __call__(self, *args: Expression) -> Any: # <3> + local_env = dict(zip(self.parms, args)) # <4> + env = Environment(local_env, self.env) # <5> + for exp in self.body: # <6> + result = evaluate(exp, env) + return result # <7> +# end::PROCEDURE[] -################ non-interactive execution +################ command-line interface -def run_lines(source: str) -> Iterator[Any]: - global_env: Environment = standard_env() +def run(source: str) -> Any: + global_env = standard_env() tokens = tokenize(source) while tokens: exp = read_from_tokens(tokens) - yield evaluate(exp, global_env) + result = evaluate(exp, global_env) + return result +def main(args: list[str]) -> None: + if len(args) == 1: + with open(args[0]) as fp: + run(fp.read()) + else: + repl() -def run(source: str) -> Any: - for result in run_lines(source): - pass - return result +if __name__ == '__main__': + import sys + main(sys.argv[1:]) diff --git a/02-array-seq/lispy/py3.10/lis_test.py b/18-with-match/lispy/py3.10/lis_test.py similarity index 100% rename from 02-array-seq/lispy/py3.10/lis_test.py rename to 18-with-match/lispy/py3.10/lis_test.py diff --git a/18-with-match/lispy/py3.10/quicksort.scm b/18-with-match/lispy/py3.10/quicksort.scm new file mode 100644 index 0000000..08dd596 --- /dev/null +++ b/18-with-match/lispy/py3.10/quicksort.scm @@ -0,0 +1,17 @@ +(define (quicksort lst) + (if (null? lst) + lst + (begin + (define pivot (car lst)) + (define rest (cdr lst)) + (append + (quicksort + (filter (lambda (x) (< x pivot)) rest)) + (list pivot) + (quicksort + (filter (lambda (x) (>= x pivot)) rest))) + ) + ) +) +(display + (quicksort (list 2 1 6 3 4 0 8 9 7 5))) diff --git a/02-array-seq/lispy/py3.9/README.md b/18-with-match/lispy/py3.9/README.md similarity index 100% rename from 02-array-seq/lispy/py3.9/README.md rename to 18-with-match/lispy/py3.9/README.md diff --git a/02-array-seq/lispy/py3.9/lis.py b/18-with-match/lispy/py3.9/lis.py similarity index 91% rename from 02-array-seq/lispy/py3.9/lis.py rename to 18-with-match/lispy/py3.9/lis.py index 11f4402..9e4dec1 100644 --- a/02-array-seq/lispy/py3.9/lis.py +++ b/18-with-match/lispy/py3.9/lis.py @@ -140,16 +140,18 @@ def evaluate(x: Expression, env: Environment) -> Any: (_, exp) = x return exp elif x[0] == 'if': # (if test conseq alt) - (_, test, conseq, alt) = x - exp = (conseq if evaluate(test, env) else alt) - return evaluate(exp, env) - elif x[0] == 'define': # (define var exp) - (_, var, exp) = x - env[var] = evaluate(exp, env) - elif x[0] == 'lambda': # (lambda (var...) body) + (_, test, consequence, alternative) = x + if evaluate(test, env): + return evaluate(consequence, env) + else: + return evaluate(alternative, env) + elif x[0] == 'define': # (define name exp) + (_, name, exp) = x + env[name] = evaluate(exp, env) + elif x[0] == 'lambda': # (lambda (parm…) body) (_, parms, *body) = x return Procedure(parms, body, env) - else: # (proc arg...) + else: # (proc arg…) proc = evaluate(x[0], env) args = [evaluate(exp, env) for exp in x[1:]] return proc(*args) diff --git a/02-array-seq/lispy/py3.9/lis_test.py b/18-with-match/lispy/py3.9/lis_test.py similarity index 100% rename from 02-array-seq/lispy/py3.9/lis_test.py rename to 18-with-match/lispy/py3.9/lis_test.py diff --git a/18-with-match/mirror.py b/18-with-match/mirror.py index ba31944..841c6ab 100644 --- a/18-with-match/mirror.py +++ b/18-with-match/mirror.py @@ -11,11 +11,11 @@ ... print('Alice, Kitty and Snowdrop') # <2> ... print(what) ... - pordwonS dna yttiK ,ecilA # <3> + pordwonS dna yttiK ,ecilA YKCOWREBBAJ - >>> what # <4> + >>> what # <3> 'JABBERWOCKY' - >>> print('Back to normal.') # <5> + >>> print('Back to normal.') # <4> Back to normal. # end::MIRROR_DEMO_1[] @@ -27,15 +27,15 @@ >>> from mirror import LookingGlass >>> manager = LookingGlass() # <1> - >>> manager - + >>> manager # doctest: +ELLIPSIS + >>> monster = manager.__enter__() # <2> >>> monster == 'JABBERWOCKY' # <3> eurT >>> monster 'YKCOWREBBAJ' - >>> manager - >ca875a2x0 ta tcejbo ssalGgnikooL.rorrim< + >>> manager # doctest: +ELLIPSIS + >... ta tcejbo ssalGgnikooL.rorrim< >>> manager.__exit__(None, None, None) # <4> >>> monster 'JABBERWOCKY' @@ -69,10 +69,11 @@ # tag::MIRROR_EX[] +import sys + class LookingGlass: def __enter__(self): # <1> - import sys self.original_write = sys.stdout.write # <2> sys.stdout.write = self.reverse_write # <3> return 'JABBERWOCKY' # <4> @@ -81,12 +82,9 @@ def reverse_write(self, text): # <5> self.original_write(text[::-1]) def __exit__(self, exc_type, exc_value, traceback): # <6> - import sys # <7> - sys.stdout.write = self.original_write # <8> - if exc_type is ZeroDivisionError: # <9> + sys.stdout.write = self.original_write # <7> + if exc_type is ZeroDivisionError: # <8> print('Please DO NOT divide by zero!') - return True # <10> - # <11> - - + return True # <9> + # <10> # end::MIRROR_EX[] diff --git a/18-with-match/mirror_gen.py b/18-with-match/mirror_gen.py index 457955a..6dfaf02 100644 --- a/18-with-match/mirror_gen.py +++ b/18-with-match/mirror_gen.py @@ -35,22 +35,36 @@ >>> manager # doctest: +ELLIPSIS >...x0 ta tcejbo reganaMtxetnoCrotareneG_.biltxetnoc< >>> manager.__exit__(None, None, None) # <4> + False >>> monster 'JABBERWOCKY' # end::MIRROR_GEN_DEMO_2[] +The decorated generator also works as a decorator: + + +# tag::MIRROR_GEN_DECO[] + >>> @looking_glass() + ... def verse(): + ... print('The time has come') + ... + >>> verse() # <1> + emoc sah emit ehT + >>> print('back to normal') # <2> + back to normal + +# end::MIRROR_GEN_DECO[] + """ # tag::MIRROR_GEN_EX[] - import contextlib - +import sys @contextlib.contextmanager # <1> def looking_glass(): - import sys original_write = sys.stdout.write # <2> def reverse_write(text): # <3> @@ -59,6 +73,4 @@ def reverse_write(text): # <3> sys.stdout.write = reverse_write # <4> yield 'JABBERWOCKY' # <5> sys.stdout.write = original_write # <6> - - # end::MIRROR_GEN_EX[] diff --git a/18-with-match/mirror_gen_exc.py b/18-with-match/mirror_gen_exc.py index c446918..0c20a3b 100644 --- a/18-with-match/mirror_gen_exc.py +++ b/18-with-match/mirror_gen_exc.py @@ -35,6 +35,7 @@ >>> manager # doctest: +ELLIPSIS >...x0 ta tcejbo reganaMtxetnoCrotareneG_.biltxetnoc< >>> manager.__exit__(None, None, None) # <4> + False >>> monster 'JABBERWOCKY' @@ -48,7 +49,7 @@ # tag::MIRROR_GEN_DEMO_3[] - >>> from mirror_gen import looking_glass + >>> from mirror_gen_exc import looking_glass >>> with looking_glass(): ... print('Humpty Dumpty') ... x = 1/0 # <1> @@ -74,13 +75,11 @@ # tag::MIRROR_GEN_EXC[] - import contextlib - +import sys @contextlib.contextmanager def looking_glass(): - import sys original_write = sys.stdout.write def reverse_write(text): @@ -96,6 +95,4 @@ def reverse_write(text): sys.stdout.write = original_write # <3> if msg: print(msg) # <4> - - # end::MIRROR_GEN_EXC[] diff --git a/20-futures/getflags/flags2_asyncio.py b/20-futures/getflags/flags2_asyncio.py index e7a73ac..88a18ed 100755 --- a/20-futures/getflags/flags2_asyncio.py +++ b/20-futures/getflags/flags2_asyncio.py @@ -36,10 +36,10 @@ async def get_flag(session: aiohttp.ClientSession, # <2> resp.raise_for_status() # <3> return bytes() -async def download_one(session: aiohttp.ClientSession, # <4> +async def download_one(session: aiohttp.ClientSession, cc: str, base_url: str, - semaphore: asyncio.Semaphore, + semaphore: asyncio.Semaphore, # <4> verbose: bool) -> Result: try: async with semaphore: # <5> diff --git a/20-futures/getflags/flags3_asyncio.py b/20-futures/getflags/flags3_asyncio.py index 37b5957..04a01e2 100755 --- a/20-futures/getflags/flags3_asyncio.py +++ b/20-futures/getflags/flags3_asyncio.py @@ -36,9 +36,9 @@ async def get_flag(session: aiohttp.ClientSession, return bytes() # tag::FLAGS3_ASYNCIO_GET_COUNTRY[] -async def get_country(session: aiohttp.ClientSession, # <1> +async def get_country(session: aiohttp.ClientSession, base_url: str, - cc: str) -> str: + cc: str) -> str: # <1> url = f'{base_url}/{cc}/metadata.json' async with session.get(url) as resp: if resp.status == 200: diff --git a/20-futures/getflags/requirements.txt b/20-futures/getflags/requirements.txt index 793679b..37baa8d 100644 --- a/20-futures/getflags/requirements.txt +++ b/20-futures/getflags/requirements.txt @@ -1,11 +1,13 @@ -aiohttp==3.7.4 +aiohttp==3.7.4.post0 async-timeout==3.0.1 -attrs==20.3.0 -certifi==2020.12.5 +attrs==21.2.0 +certifi==2021.5.30 chardet==4.0.0 -idna==2.10 -requests==2.25.1 -urllib3==1.26.5 -tqdm==4.56.2 +charset-normalizer==2.0.4 +idna==3.2 multidict==5.1.0 +requests==2.26.0 +tqdm==4.62.2 +typing-extensions==3.10.0.2 +urllib3==1.26.6 yarl==1.6.3 diff --git a/20-futures/getflags/slow_server.py b/20-futures/getflags/slow_server.py index 9d8142b..bede010 100755 --- a/20-futures/getflags/slow_server.py +++ b/20-futures/getflags/slow_server.py @@ -38,6 +38,7 @@ def do_GET(self): """Serve a GET request.""" time.sleep(.5) if random() < self.error_rate: + # HTTPStatus.IM_A_TEAPOT requires Python >= 3.9 self.send_error(HTTPStatus.IM_A_TEAPOT, "I'm a Teapot") else: f = self.send_head() diff --git a/22-dyn-attr-prop/oscon/demo_schedule2.py b/22-dyn-attr-prop/oscon/demo_schedule2.py deleted file mode 100755 index 12fd440..0000000 --- a/22-dyn-attr-prop/oscon/demo_schedule2.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python3 - -import shelve - -from schedule_v2 import DB_NAME, CONFERENCE, load_db -from schedule_v2 import DbRecord, Event - -with shelve.open(DB_NAME) as db: - if CONFERENCE not in db: - load_db(db) - - DbRecord.set_db(db) - event = DbRecord.fetch('event.33950') - print(event) - print(event.venue) - print(event.venue.name) - for spkr in event.speakers: - print(f'{spkr.serial}:', spkr.name) - - print(repr(Event.venue)) - - event2 = DbRecord.fetch('event.33451') - print(event2) - print(event2.fetch) - print(event2.venue) \ No newline at end of file From 3ecfb212c6273122797c76876d6b373b2cb94fa6 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 18 Sep 2021 13:18:17 -0300 Subject: [PATCH 068/127] updated from Atlas --- 18-with-match/lispy/py3.10/examples_test.py | 43 ++-- 18-with-match/lispy/py3.10/lis.py | 18 +- 18-with-match/lispy/py3.10/lis_test.py | 6 +- 18-with-match/lispy/py3.9/examples_test.py | 258 ++++++++++++++++++++ 18-with-match/lispy/py3.9/lis.py | 207 ++++++++++------ 18-with-match/lispy/py3.9/lis_test.py | 20 +- 6 files changed, 438 insertions(+), 114 deletions(-) create mode 100644 18-with-match/lispy/py3.9/examples_test.py diff --git a/18-with-match/lispy/py3.10/examples_test.py b/18-with-match/lispy/py3.10/examples_test.py index ed4b68a..ba6bd10 100644 --- a/18-with-match/lispy/py3.10/examples_test.py +++ b/18-with-match/lispy/py3.10/examples_test.py @@ -2,16 +2,12 @@ Doctests for `parse` -------------------- -# tag::PARSE_ATOM[] +# tag::PARSE[] >>> from lis import parse >>> parse('1.5') 1.5 >>> parse('ni!') 'ni!' - -# end::PARSE_ATOM[] - -# tag::PARSE_LIST[] >>> parse('(gcd 18 45)') ['gcd', 18, 45] >>> parse(''' @@ -21,15 +17,15 @@ ... ''') ['define', 'double', ['lambda', ['n'], ['*', 'n', 2]]] -# end::PARSE_LIST[] +# end::PARSE[] Doctest for `Environment` ------------------------- # tag::ENVIRONMENT[] >>> from lis import Environment ->>> outer_env = {'a': 0, 'b': 1} >>> inner_env = {'a': 2} +>>> outer_env = {'a': 0, 'b': 1} >>> env = Environment(inner_env, outer_env) >>> env['a'] = 111 # <1> >>> env['c'] = 222 @@ -64,11 +60,11 @@ # tag::EVAL_QUOTE[] ->>> evaluate(parse('(quote no-such-name)'), {}) +>>> evaluate(parse('(quote no-such-name)'), standard_env()) 'no-such-name' ->>> evaluate(parse('(quote (99 bottles of beer))'), {}) +>>> evaluate(parse('(quote (99 bottles of beer))'), standard_env()) [99, 'bottles', 'of', 'beer'] ->>> evaluate(parse('(quote (/ 10 0))'), {}) +>>> evaluate(parse('(quote (/ 10 0))'), standard_env()) ['/', 10, 0] # end::EVAL_QUOTE[] @@ -156,11 +152,12 @@ def test_factorial(): (if (= n 0) m (gcd n (mod m n)))) -(gcd 18 45) +(display (gcd 18 45)) """ -def test_gcd(): - got = run(gcd_src) - assert got == 9 +def test_gcd(capsys): + run(gcd_src) + captured = capsys.readouterr() + assert captured.out == '9\n' quicksort_src = """ @@ -216,7 +213,7 @@ def test_newton(): (define inc (make-adder 1)) (inc 99) """ -def test_newton(): +def test_closure(): got = run(closure_src) assert got == 100 @@ -228,13 +225,15 @@ def test_newton(): n) ) (define counter (make-counter)) -(counter) -(counter) -(counter) +(display (counter)) +(display (counter)) +(display (counter)) """ -def test_closure_with_change(): - got = run(closure_with_change_src) - assert got == 3 +def test_closure_with_change(capsys): + run(closure_with_change_src) + captured = capsys.readouterr() + assert captured.out == '1\n2\n3\n' + # tag::RUN_AVERAGER[] @@ -256,4 +255,4 @@ def test_closure_with_change(): def test_closure_averager(): got = run(closure_averager_src) assert got == 12.0 -# end::RUN_AVERAGER[] \ No newline at end of file +# end::RUN_AVERAGER[] diff --git a/18-with-match/lispy/py3.10/lis.py b/18-with-match/lispy/py3.10/lis.py index 4a5195c..ff494d1 100755 --- a/18-with-match/lispy/py3.10/lis.py +++ b/18-with-match/lispy/py3.10/lis.py @@ -58,10 +58,11 @@ def parse_atom(token: str) -> Atom: except ValueError: return Symbol(token) + ################ Global Environment # tag::ENV_CLASS[] -class Environment(ChainMap): +class Environment(ChainMap[Symbol, Any]): "A ChainMap that allows changing an item in-place." def change(self, key: Symbol, value: object) -> None: @@ -119,11 +120,11 @@ def standard_env() -> Environment: ################ Interaction: A REPL # tag::REPL[] -def repl() -> NoReturn: +def repl(prompt: str = 'lis.py> ') -> NoReturn: "A prompt-read-eval-print loop." global_env = standard_env() while True: - ast = parse(input('lis.py> ')) + ast = parse(input(prompt)) val = evaluate(ast, global_env) if val is not None: print(lispstr(val)) @@ -140,7 +141,10 @@ def lispstr(exp: object) -> str: ################ Evaluator # tag::EVALUATE[] -KEYWORDS = ['quote', 'if', 'lambda', 'define', 'set!'] +KEYWORDS = {'quote', 'if', 'lambda', 'define', 'set!'} + +def is_keyword(s: Any) -> bool: + return isinstance(s, Symbol) and s in KEYWORDS def evaluate(exp: Expression, env: Environment) -> Any: "Evaluate an expression in an environment." @@ -149,8 +153,6 @@ def evaluate(exp: Expression, env: Environment) -> Any: return x case Symbol(var): return env[var] - case []: - return [] case ['quote', x]: return x case ['if', test, consequence, alternative]: @@ -166,8 +168,8 @@ def evaluate(exp: Expression, env: Environment) -> Any: env[name] = Procedure(parms, body, env) case ['set!', Symbol(var), value_exp]: env.change(var, evaluate(value_exp, env)) - case [op, *args] if op not in KEYWORDS: - proc = evaluate(op, env) + case [func_exp, *args] if not is_keyword(func_exp): + proc = evaluate(func_exp, env) values = [evaluate(arg, env) for arg in args] return proc(*values) case _: diff --git a/18-with-match/lispy/py3.10/lis_test.py b/18-with-match/lispy/py3.10/lis_test.py index 3688888..03dc15c 100644 --- a/18-with-match/lispy/py3.10/lis_test.py +++ b/18-with-match/lispy/py3.10/lis_test.py @@ -73,10 +73,10 @@ def test_evaluate(source: str, expected: Optional[Expression]) -> None: def std_env() -> Environment: return standard_env() -# tests for each of the cases in evaluate +# tests for cases in evaluate def test_evaluate_variable() -> None: - env: Environment = dict(x=10) + env = Environment({'x': 10}) source = 'x' expected = 10 got = evaluate(parse(source), env) @@ -168,8 +168,6 @@ def test_invocation_user_procedure(std_env: Environment) -> None: assert got == 22 -###################################### for py3.10/lis.py only - def test_define_function(std_env: Environment) -> None: source = '(define (max a b) (if (>= a b) a b))' got = evaluate(parse(source), std_env) diff --git a/18-with-match/lispy/py3.9/examples_test.py b/18-with-match/lispy/py3.9/examples_test.py new file mode 100644 index 0000000..c632662 --- /dev/null +++ b/18-with-match/lispy/py3.9/examples_test.py @@ -0,0 +1,258 @@ +""" +Doctests for `parse` +-------------------- + +# tag::PARSE[] +>>> from lis import parse +>>> parse('1.5') +1.5 +>>> parse('ni!') +'ni!' +>>> parse('(gcd 18 45)') +['gcd', 18, 45] +>>> parse(''' +... (define double +... (lambda (n) +... (* n 2))) +... ''') +['define', 'double', ['lambda', ['n'], ['*', 'n', 2]]] + +# end::PARSE[] + +Doctest for `Environment` +------------------------- + +# tag::ENVIRONMENT[] +>>> from lis import Environment +>>> inner_env = {'a': 2} +>>> outer_env = {'a': 0, 'b': 1} +>>> env = Environment(inner_env, outer_env) +>>> env['a'] = 111 # <1> +>>> env['c'] = 222 +>>> env +Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 1}) +>>> env.change('b', 333) # <2> +>>> env +Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 333}) + +# end::ENVIRONMENT[] + +Doctests for `evaluate` +----------------------- + +# tag::EVAL_NUMBER[] +>>> from lis import parse, evaluate, standard_env +>>> evaluate(parse('1.5'), {}) +1.5 + +# end::EVAL_NUMBER[] + +# tag::EVAL_SYMBOL[] +>>> from lis import standard_env +>>> evaluate(parse('+'), standard_env()) + +>>> evaluate(parse('ni!'), standard_env()) +Traceback (most recent call last): + ... +KeyError: 'ni!' + +# end::EVAL_SYMBOL[] + + +# tag::EVAL_QUOTE[] +>>> evaluate(parse('(quote no-such-name)'), standard_env()) +'no-such-name' +>>> evaluate(parse('(quote (99 bottles of beer))'), standard_env()) +[99, 'bottles', 'of', 'beer'] +>>> evaluate(parse('(quote (/ 10 0))'), standard_env()) +['/', 10, 0] + +# end::EVAL_QUOTE[] + +# tag::EVAL_IF[] +>>> evaluate(parse('(if (= 3 3) 1 0))'), standard_env()) +1 +>>> evaluate(parse('(if (= 3 4) 1 0))'), standard_env()) +0 + +# end::EVAL_IF[] + + +# tag::EVAL_LAMBDA[] +>>> expr = '(lambda (a b) (* (/ a b) 100))' +>>> f = evaluate(parse(expr), standard_env()) +>>> f # doctest: +ELLIPSIS + +>>> f(15, 20) +75.0 + +# end::EVAL_LAMBDA[] + +# tag::EVAL_DEFINE[] +>>> global_env = standard_env() +>>> evaluate(parse('(define answer (* 7 6))'), global_env) +>>> global_env['answer'] +42 + +# end::EVAL_DEFINE[] + +# tag::EVAL_DEFUN[] +>>> global_env = standard_env() +>>> percent = '(define (% a b) (* (/ a b) 100))' +>>> evaluate(parse(percent), global_env) +>>> global_env['%'] # doctest: +ELLIPSIS + +>>> global_env['%'](170, 200) +85.0 + +# end::EVAL_DEFUN[] + +function call: + +# tag::EVAL_CALL[] +>>> evaluate(parse('(% (* 12 14) (- 500 100))'), global_env) +42.0 + +# end::EVAL_CALL[] + +# tag::EVAL_SYNTAX_ERROR[] +>>> evaluate(parse('(lambda is not like this)'), standard_env()) +Traceback (most recent call last): + ... +SyntaxError: (lambda is not like this) + +# end::EVAL_SYNTAX_ERROR[] + +""" + +import math + +from lis import run + + +fact_src = """ +(define (! n) + (if (< n 2) + 1 + (* n (! (- n 1))) + ) +) +(! 42) +""" +def test_factorial(): + got = run(fact_src) + assert got == 1405006117752879898543142606244511569936384000000000 + assert got == math.factorial(42) + + +gcd_src = """ +(define (mod m n) + (- m (* n (// m n)))) +(define (gcd m n) + (if (= n 0) + m + (gcd n (mod m n)))) +(display (gcd 18 45)) +""" +def test_gcd(capsys): + run(gcd_src) + captured = capsys.readouterr() + assert captured.out == '9\n' + + +quicksort_src = """ +(define (quicksort lst) + (if (null? lst) + lst + (begin + (define pivot (car lst)) + (define rest (cdr lst)) + (append + (quicksort + (filter (lambda (x) (< x pivot)) rest)) + (list pivot) + (quicksort + (filter (lambda (x) (>= x pivot)) rest))) + ) + ) +) +(quicksort (list 2 1 6 3 4 0 8 9 7 5)) +""" +def test_quicksort(): + got = run(quicksort_src) + assert got == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + +# Example from Structure and Interpretation of Computer Programs +# https://mitpress.mit.edu/sites/default/files/sicp/full-text/sicp/book/node12.html + +newton_src = """ +(define (sqrt x) + (sqrt-iter 1.0 x)) +(define (sqrt-iter guess x) + (if (good-enough? guess x) + guess + (sqrt-iter (improve guess x) x))) +(define (good-enough? guess x) + (< (abs (- (* guess guess) x)) 0.001)) +(define (improve guess x) + (average guess (/ x guess))) +(define (average x y) + (/ (+ x y) 2)) +(sqrt 123454321) +""" +def test_newton(): + got = run(newton_src) + assert math.isclose(got, 11111) + + +closure_src = """ +(define (make-adder increment) + (lambda (x) (+ increment x)) +) +(define inc (make-adder 1)) +(inc 99) +""" +def test_closure(): + got = run(closure_src) + assert got == 100 + +closure_with_change_src = """ +(define (make-counter) + (define n 0) + (lambda () + (set! n (+ n 1)) + n) +) +(define counter (make-counter)) +(display (counter)) +(display (counter)) +(display (counter)) +""" +def test_closure_with_change(capsys): + run(closure_with_change_src) + captured = capsys.readouterr() + assert captured.out == '1\n2\n3\n' + + + +# tag::RUN_AVERAGER[] +closure_averager_src = """ +(define (make-averager) + (define count 0) + (define total 0) + (lambda (new-value) + (set! count (+ count 1)) + (set! total (+ total new-value)) + (/ total count) + ) +) +(define avg (make-averager)) +(avg 10) +(avg 11) +(avg 15) +""" +def test_closure_averager(): + got = run(closure_averager_src) + assert got == 12.0 +# end::RUN_AVERAGER[] \ No newline at end of file diff --git a/18-with-match/lispy/py3.9/lis.py b/18-with-match/lispy/py3.9/lis.py index 9e4dec1..e8881cd 100644 --- a/18-with-match/lispy/py3.9/lis.py +++ b/18-with-match/lispy/py3.9/lis.py @@ -1,73 +1,24 @@ +#!/usr/bin/env python + ################ Lispy: Scheme Interpreter in Python 3.9 ## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html ## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) ## by Luciano Ramalho, adding type hints and pattern matching. + ################ Imports and Types import math import operator as op from collections import ChainMap -from collections.abc import MutableMapping, Iterator from itertools import chain -from typing import Any, Union +from typing import Any, Union, NoReturn Symbol = str Atom = Union[float, int, Symbol] Expression = Union[Atom, list] -Environment = MutableMapping[Symbol, object] - - -class Procedure: - "A user-defined Scheme procedure." - - def __init__(self, parms: list[Symbol], body: list[Expression], env: Environment): - self.parms = parms - self.body = body - self.env = env - - def __call__(self, *args: Expression) -> Any: - local_env = dict(zip(self.parms, args)) - env: Environment = ChainMap(local_env, self.env) - for exp in self.body: - result = evaluate(exp, env) - return result - - -################ Global Environment - -def standard_env() -> Environment: - "An environment with some Scheme standard procedures." - env: Environment = {} - env.update(vars(math)) # sin, cos, sqrt, pi, ... - env.update({ - '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, - '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, - 'abs': abs, - 'append': op.add, - 'apply': lambda proc, args: proc(*args), - 'begin': lambda *x: x[-1], - 'car': lambda x: x[0], - 'cdr': lambda x: x[1:], - 'cons': lambda x,y: [x] + y, - 'eq?': op.is_, - 'equal?': op.eq, - 'length': len, - 'list': lambda *x: list(x), - 'list?': lambda x: isinstance(x,list), - 'map': lambda *args: list(map(*args)), - 'max': max, - 'min': min, - 'not': op.not_, - 'null?': lambda x: x == [], - 'number?': lambda x: isinstance(x, (int, float)), - 'procedure?': callable, - 'round': round, - 'symbol?': lambda x: isinstance(x, Symbol), - }) - return env ################ Parsing: parse, tokenize, and read_from_tokens @@ -75,12 +26,10 @@ def parse(program: str) -> Expression: "Read a Scheme expression from a string." return read_from_tokens(tokenize(program)) - def tokenize(s: str) -> list[str]: "Convert a string into a list of tokens." return s.replace('(', ' ( ').replace(')', ' ) ').split() - def read_from_tokens(tokens: list[str]) -> Expression: "Read an expression from a sequence of tokens." if len(tokens) == 0: @@ -97,7 +46,6 @@ def read_from_tokens(tokens: list[str]) -> Expression: else: return parse_atom(token) - def parse_atom(token: str) -> Atom: "Numbers become numbers; every other token is a symbol." try: @@ -109,17 +57,73 @@ def parse_atom(token: str) -> Atom: return Symbol(token) +################ Global Environment + +class Environment(ChainMap[Symbol, Any]): + "A ChainMap that allows changing an item in-place." + + def change(self, key: Symbol, value: object) -> None: + "Find where key is defined and change the value there." + for map in self.maps: + if key in map: + map[key] = value # type: ignore[index] + return + raise KeyError(key) + + +def standard_env() -> Environment: + "An environment with some Scheme standard procedures." + env = Environment() + env.update(vars(math)) # sin, cos, sqrt, pi, ... + env.update({ + '+': op.add, + '-': op.sub, + '*': op.mul, + '/': op.truediv, + '//': op.floordiv, + '>': op.gt, + '<': op.lt, + '>=': op.ge, + '<=': op.le, + '=': op.eq, + 'abs': abs, + 'append': lambda *args: list(chain(*args)), + 'apply': lambda proc, args: proc(*args), + 'begin': lambda *x: x[-1], + 'car': lambda x: x[0], + 'cdr': lambda x: x[1:], + 'cons': lambda x, y: [x] + y, + 'display': lambda x: print(lispstr(x)), + 'eq?': op.is_, + 'equal?': op.eq, + 'filter': lambda *args: list(filter(*args)), + 'length': len, + 'list': lambda *x: list(x), + 'list?': lambda x: isinstance(x, list), + 'map': lambda *args: list(map(*args)), + 'max': max, + 'min': min, + 'not': op.not_, + 'null?': lambda x: x == [], + 'number?': lambda x: isinstance(x, (int, float)), + 'procedure?': callable, + 'round': round, + 'symbol?': lambda x: isinstance(x, Symbol), + }) + return env + + ################ Interaction: A REPL -def repl(prompt: str = 'lis.py> ') -> None: +def repl(prompt: str = 'lis.py> ') -> NoReturn: "A prompt-read-eval-print loop." global_env = standard_env() while True: - val = evaluate(parse(input(prompt)), global_env) + ast = parse(input(prompt)) + val = evaluate(ast, global_env) if val is not None: print(lispstr(val)) - def lispstr(exp: object) -> str: "Convert a Python object back into a Lisp-readable string." if isinstance(exp, list): @@ -128,30 +132,81 @@ def lispstr(exp: object) -> str: return str(exp) -################ eval +################ Evaluator -def evaluate(x: Expression, env: Environment) -> Any: +def evaluate(exp: Expression, env: Environment) -> Any: "Evaluate an expression in an environment." - if isinstance(x, Symbol): # variable reference - return env[x] - elif not isinstance(x, list): # constant literal - return x - elif x[0] == 'quote': # (quote exp) - (_, exp) = x + if isinstance(exp, Symbol): # variable reference + return env[exp] + elif not isinstance(exp, list): # constant literal return exp - elif x[0] == 'if': # (if test conseq alt) - (_, test, consequence, alternative) = x + elif exp[0] == 'quote': # (quote exp) + (_, x) = exp + return x + elif exp[0] == 'if': # (if test conseq alt) + (_, test, consequence, alternative) = exp if evaluate(test, env): return evaluate(consequence, env) else: return evaluate(alternative, env) - elif x[0] == 'define': # (define name exp) - (_, name, exp) = x - env[name] = evaluate(exp, env) - elif x[0] == 'lambda': # (lambda (parm…) body) - (_, parms, *body) = x + elif exp[0] == 'lambda': # (lambda (parm…) body…) + (_, parms, *body) = exp + if not isinstance(parms, list): + raise SyntaxError(lispstr(exp)) return Procedure(parms, body, env) + elif exp[0] == 'define': + (_, name_exp, *rest) = exp + if isinstance(name_exp, Symbol): # (define name exp) + value_exp = rest[0] + env[name_exp] = evaluate(value_exp, env) + else: # (define (name parm…) body…) + name, *parms = name_exp + env[name] = Procedure(parms, rest, env) + elif exp[0] == 'set!': + (_, var, value_exp) = exp + env.change(var, evaluate(value_exp, env)) else: # (proc arg…) - proc = evaluate(x[0], env) - args = [evaluate(exp, env) for exp in x[1:]] + (func_exp, *args) = exp + proc = evaluate(func_exp, env) + args = [evaluate(arg, env) for arg in args] return proc(*args) + + +class Procedure: + "A user-defined Scheme procedure." + + def __init__( + self, parms: list[Symbol], body: list[Expression], env: Environment + ): + self.parms = parms + self.body = body + self.env = env + + def __call__(self, *args: Expression) -> Any: + local_env = dict(zip(self.parms, args)) + env = Environment(local_env, self.env) + for exp in self.body: + result = evaluate(exp, env) + return result + + +################ command-line interface + +def run(source: str) -> Any: + global_env = standard_env() + tokens = tokenize(source) + while tokens: + exp = read_from_tokens(tokens) + result = evaluate(exp, global_env) + return result + +def main(args: list[str]) -> None: + if len(args) == 1: + with open(args[0]) as fp: + run(fp.read()) + else: + repl() + +if __name__ == '__main__': + import sys + main(sys.argv[1:]) diff --git a/18-with-match/lispy/py3.9/lis_test.py b/18-with-match/lispy/py3.9/lis_test.py index 106ac24..03dc15c 100644 --- a/18-with-match/lispy/py3.9/lis_test.py +++ b/18-with-match/lispy/py3.9/lis_test.py @@ -1,8 +1,8 @@ -from typing import Any, Optional +from typing import Optional from pytest import mark, fixture -from lis import parse, evaluate, standard_env, Symbol, Environment, Expression +from lis import parse, evaluate, Expression, Environment, standard_env ############################################################# tests for parse @@ -73,10 +73,10 @@ def test_evaluate(source: str, expected: Optional[Expression]) -> None: def std_env() -> Environment: return standard_env() -# tests for each of the cases in evaluate +# tests for cases in evaluate def test_evaluate_variable() -> None: - env: Environment = dict(x=10) + env = Environment({'x': 10}) source = 'x' expected = 10 got = evaluate(parse(source), env) @@ -166,3 +166,15 @@ def test_invocation_user_procedure(std_env: Environment) -> None: """ got = evaluate(parse(source), std_env) assert got == 22 + + +def test_define_function(std_env: Environment) -> None: + source = '(define (max a b) (if (>= a b) a b))' + got = evaluate(parse(source), std_env) + assert got is None + max_fn = std_env['max'] + assert max_fn.parms == ['a', 'b'] + assert max_fn.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] + assert max_fn.env is std_env + assert max_fn(1, 2) == 2 + assert max_fn(3, 2) == 3 From 2f2f87d4fb297869cb5763ec9b2ffcad3e841d20 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 20 Sep 2021 10:37:26 -0300 Subject: [PATCH 069/127] sync from Atlas --- 02-array-seq/lispy/py3.10/examples_test.py | 258 ++++++++++++++++++++ 02-array-seq/lispy/py3.10/lis.py | 219 +++++++++++++++++ 02-array-seq/lispy/py3.10/lis_test.py | 180 ++++++++++++++ 02-array-seq/lispy/py3.10/quicksort.scm | 17 ++ 02-array-seq/lispy/py3.9/README.md | 33 +++ 02-array-seq/lispy/py3.9/examples_test.py | 196 +++++++++++++++ 02-array-seq/lispy/py3.9/lis.py | 209 ++++++++++++++++ 02-array-seq/lispy/py3.9/lis_test.py | 180 ++++++++++++++ 18-with-match/lispy/py3.10/examples_test.py | 43 ++-- 18-with-match/lispy/py3.10/lis.py | 14 +- 18-with-match/lispy/py3.10/lis_test.py | 6 +- 18-with-match/lispy/py3.9/examples_test.py | 258 ++++++++++++++++++++ 18-with-match/lispy/py3.9/lis.py | 207 ++++++++++------ 18-with-match/lispy/py3.9/lis_test.py | 20 +- 18-with-match/mirror_gen.py | 3 + 22-dyn-attr-prop/doc_property.py | 2 +- 16 files changed, 1730 insertions(+), 115 deletions(-) create mode 100644 02-array-seq/lispy/py3.10/examples_test.py create mode 100755 02-array-seq/lispy/py3.10/lis.py create mode 100644 02-array-seq/lispy/py3.10/lis_test.py create mode 100644 02-array-seq/lispy/py3.10/quicksort.scm create mode 100644 02-array-seq/lispy/py3.9/README.md create mode 100644 02-array-seq/lispy/py3.9/examples_test.py create mode 100644 02-array-seq/lispy/py3.9/lis.py create mode 100644 02-array-seq/lispy/py3.9/lis_test.py create mode 100644 18-with-match/lispy/py3.9/examples_test.py diff --git a/02-array-seq/lispy/py3.10/examples_test.py b/02-array-seq/lispy/py3.10/examples_test.py new file mode 100644 index 0000000..ba6bd10 --- /dev/null +++ b/02-array-seq/lispy/py3.10/examples_test.py @@ -0,0 +1,258 @@ +""" +Doctests for `parse` +-------------------- + +# tag::PARSE[] +>>> from lis import parse +>>> parse('1.5') +1.5 +>>> parse('ni!') +'ni!' +>>> parse('(gcd 18 45)') +['gcd', 18, 45] +>>> parse(''' +... (define double +... (lambda (n) +... (* n 2))) +... ''') +['define', 'double', ['lambda', ['n'], ['*', 'n', 2]]] + +# end::PARSE[] + +Doctest for `Environment` +------------------------- + +# tag::ENVIRONMENT[] +>>> from lis import Environment +>>> inner_env = {'a': 2} +>>> outer_env = {'a': 0, 'b': 1} +>>> env = Environment(inner_env, outer_env) +>>> env['a'] = 111 # <1> +>>> env['c'] = 222 +>>> env +Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 1}) +>>> env.change('b', 333) # <2> +>>> env +Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 333}) + +# end::ENVIRONMENT[] + +Doctests for `evaluate` +----------------------- + +# tag::EVAL_NUMBER[] +>>> from lis import parse, evaluate, standard_env +>>> evaluate(parse('1.5'), {}) +1.5 + +# end::EVAL_NUMBER[] + +# tag::EVAL_SYMBOL[] +>>> from lis import standard_env +>>> evaluate(parse('+'), standard_env()) + +>>> evaluate(parse('ni!'), standard_env()) +Traceback (most recent call last): + ... +KeyError: 'ni!' + +# end::EVAL_SYMBOL[] + + +# tag::EVAL_QUOTE[] +>>> evaluate(parse('(quote no-such-name)'), standard_env()) +'no-such-name' +>>> evaluate(parse('(quote (99 bottles of beer))'), standard_env()) +[99, 'bottles', 'of', 'beer'] +>>> evaluate(parse('(quote (/ 10 0))'), standard_env()) +['/', 10, 0] + +# end::EVAL_QUOTE[] + +# tag::EVAL_IF[] +>>> evaluate(parse('(if (= 3 3) 1 0))'), standard_env()) +1 +>>> evaluate(parse('(if (= 3 4) 1 0))'), standard_env()) +0 + +# end::EVAL_IF[] + + +# tag::EVAL_LAMBDA[] +>>> expr = '(lambda (a b) (* (/ a b) 100))' +>>> f = evaluate(parse(expr), standard_env()) +>>> f # doctest: +ELLIPSIS + +>>> f(15, 20) +75.0 + +# end::EVAL_LAMBDA[] + +# tag::EVAL_DEFINE[] +>>> global_env = standard_env() +>>> evaluate(parse('(define answer (* 7 6))'), global_env) +>>> global_env['answer'] +42 + +# end::EVAL_DEFINE[] + +# tag::EVAL_DEFUN[] +>>> global_env = standard_env() +>>> percent = '(define (% a b) (* (/ a b) 100))' +>>> evaluate(parse(percent), global_env) +>>> global_env['%'] # doctest: +ELLIPSIS + +>>> global_env['%'](170, 200) +85.0 + +# end::EVAL_DEFUN[] + +function call: + +# tag::EVAL_CALL[] +>>> evaluate(parse('(% (* 12 14) (- 500 100))'), global_env) +42.0 + +# end::EVAL_CALL[] + +# tag::EVAL_SYNTAX_ERROR[] +>>> evaluate(parse('(lambda is not like this)'), standard_env()) +Traceback (most recent call last): + ... +SyntaxError: (lambda is not like this) + +# end::EVAL_SYNTAX_ERROR[] + +""" + +import math + +from lis import run + + +fact_src = """ +(define (! n) + (if (< n 2) + 1 + (* n (! (- n 1))) + ) +) +(! 42) +""" +def test_factorial(): + got = run(fact_src) + assert got == 1405006117752879898543142606244511569936384000000000 + assert got == math.factorial(42) + + +gcd_src = """ +(define (mod m n) + (- m (* n (// m n)))) +(define (gcd m n) + (if (= n 0) + m + (gcd n (mod m n)))) +(display (gcd 18 45)) +""" +def test_gcd(capsys): + run(gcd_src) + captured = capsys.readouterr() + assert captured.out == '9\n' + + +quicksort_src = """ +(define (quicksort lst) + (if (null? lst) + lst + (begin + (define pivot (car lst)) + (define rest (cdr lst)) + (append + (quicksort + (filter (lambda (x) (< x pivot)) rest)) + (list pivot) + (quicksort + (filter (lambda (x) (>= x pivot)) rest))) + ) + ) +) +(quicksort (list 2 1 6 3 4 0 8 9 7 5)) +""" +def test_quicksort(): + got = run(quicksort_src) + assert got == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + +# Example from Structure and Interpretation of Computer Programs +# https://mitpress.mit.edu/sites/default/files/sicp/full-text/sicp/book/node12.html + +newton_src = """ +(define (sqrt x) + (sqrt-iter 1.0 x)) +(define (sqrt-iter guess x) + (if (good-enough? guess x) + guess + (sqrt-iter (improve guess x) x))) +(define (good-enough? guess x) + (< (abs (- (* guess guess) x)) 0.001)) +(define (improve guess x) + (average guess (/ x guess))) +(define (average x y) + (/ (+ x y) 2)) +(sqrt 123454321) +""" +def test_newton(): + got = run(newton_src) + assert math.isclose(got, 11111) + + +closure_src = """ +(define (make-adder increment) + (lambda (x) (+ increment x)) +) +(define inc (make-adder 1)) +(inc 99) +""" +def test_closure(): + got = run(closure_src) + assert got == 100 + +closure_with_change_src = """ +(define (make-counter) + (define n 0) + (lambda () + (set! n (+ n 1)) + n) +) +(define counter (make-counter)) +(display (counter)) +(display (counter)) +(display (counter)) +""" +def test_closure_with_change(capsys): + run(closure_with_change_src) + captured = capsys.readouterr() + assert captured.out == '1\n2\n3\n' + + + +# tag::RUN_AVERAGER[] +closure_averager_src = """ +(define (make-averager) + (define count 0) + (define total 0) + (lambda (new-value) + (set! count (+ count 1)) + (set! total (+ total new-value)) + (/ total count) + ) +) +(define avg (make-averager)) +(avg 10) +(avg 11) +(avg 15) +""" +def test_closure_averager(): + got = run(closure_averager_src) + assert got == 12.0 +# end::RUN_AVERAGER[] diff --git a/02-array-seq/lispy/py3.10/lis.py b/02-array-seq/lispy/py3.10/lis.py new file mode 100755 index 0000000..cffb9a1 --- /dev/null +++ b/02-array-seq/lispy/py3.10/lis.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python + +################ Lispy: Scheme Interpreter in Python 3.10 + +## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html +## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) +## by Luciano Ramalho, adding type hints and pattern matching. + + +################ Imports and Types + +# tag::IMPORTS[] +import math +import operator as op +from collections import ChainMap +from itertools import chain +from typing import Any, TypeAlias, NoReturn + +Symbol: TypeAlias = str +Atom: TypeAlias = float | int | Symbol +Expression: TypeAlias = Atom | list +# end::IMPORTS[] + + +################ Parsing: parse, tokenize, and read_from_tokens + +def parse(program: str) -> Expression: + "Read a Scheme expression from a string." + return read_from_tokens(tokenize(program)) + +def tokenize(s: str) -> list[str]: + "Convert a string into a list of tokens." + return s.replace('(', ' ( ').replace(')', ' ) ').split() + +def read_from_tokens(tokens: list[str]) -> Expression: + "Read an expression from a sequence of tokens." + if len(tokens) == 0: + raise SyntaxError('unexpected EOF while reading') + token = tokens.pop(0) + if '(' == token: + exp = [] + while tokens[0] != ')': + exp.append(read_from_tokens(tokens)) + tokens.pop(0) # discard ')' + return exp + elif ')' == token: + raise SyntaxError('unexpected )') + else: + return parse_atom(token) + +def parse_atom(token: str) -> Atom: + "Numbers become numbers; every other token is a symbol." + try: + return int(token) + except ValueError: + try: + return float(token) + except ValueError: + return Symbol(token) + + +################ Global Environment + +# tag::ENV_CLASS[] +class Environment(ChainMap[Symbol, Any]): + "A ChainMap that allows changing an item in-place." + + def change(self, key: Symbol, value: object) -> None: + "Find where key is defined and change the value there." + for map in self.maps: + if key in map: + map[key] = value + return + raise KeyError(key) +# end::ENV_CLASS[] + + +def standard_env() -> Environment: + "An environment with some Scheme standard procedures." + env = Environment() + env.update(vars(math)) # sin, cos, sqrt, pi, ... + env.update({ + '+': op.add, + '-': op.sub, + '*': op.mul, + '/': op.truediv, + '//': op.floordiv, + '>': op.gt, + '<': op.lt, + '>=': op.ge, + '<=': op.le, + '=': op.eq, + 'abs': abs, + 'append': lambda *args: list(chain(*args)), + 'apply': lambda proc, args: proc(*args), + 'begin': lambda *x: x[-1], + 'car': lambda x: x[0], + 'cdr': lambda x: x[1:], + 'cons': lambda x, y: [x] + y, + 'display': lambda x: print(lispstr(x)), + 'eq?': op.is_, + 'equal?': op.eq, + 'filter': lambda *args: list(filter(*args)), + 'length': len, + 'list': lambda *x: list(x), + 'list?': lambda x: isinstance(x, list), + 'map': lambda *args: list(map(*args)), + 'max': max, + 'min': min, + 'not': op.not_, + 'null?': lambda x: x == [], + 'number?': lambda x: isinstance(x, (int, float)), + 'procedure?': callable, + 'round': round, + 'symbol?': lambda x: isinstance(x, Symbol), + }) + return env + + +################ Interaction: A REPL + +# tag::REPL[] +def repl(prompt: str = 'lis.py> ') -> NoReturn: + "A prompt-read-eval-print loop." + global_env = standard_env() + while True: + ast = parse(input(prompt)) + val = evaluate(ast, global_env) + if val is not None: + print(lispstr(val)) + +def lispstr(exp: object) -> str: + "Convert a Python object back into a Lisp-readable string." + if isinstance(exp, list): + return '(' + ' '.join(map(lispstr, exp)) + ')' + else: + return str(exp) +# end::REPL[] + + +################ Evaluator + +KEYWORDS = ['quote', 'if', 'lambda', 'define', 'set!'] + +# tag::EVAL_MATCH_TOP[] +def evaluate(exp: Expression, env: Environment) -> Any: + "Evaluate an expression in an environment." + match exp: +# end::EVAL_MATCH_TOP[] + case int(x) | float(x): + return x + case Symbol(name): + return env[name] +# tag::EVAL_MATCH_MIDDLE[] + case ['quote', x]: # <1> + return x + case ['if', test, consequence, alternative]: # <2> + if evaluate(test, env): + return evaluate(consequence, env) + else: + return evaluate(alternative, env) + case ['lambda', [*parms], *body] if body: # <3> + return Procedure(parms, body, env) + case ['define', Symbol(name), value_exp]: # <4> + env[name] = evaluate(value_exp, env) +# end::EVAL_MATCH_MIDDLE[] + case ['define', [Symbol(name), *parms], *body] if body: + env[name] = Procedure(parms, body, env) + case ['set!', Symbol(name), value_exp]: + env.change(name, evaluate(value_exp, env)) + case [func_exp, *args] if func_exp not in KEYWORDS: + proc = evaluate(func_exp, env) + values = [evaluate(arg, env) for arg in args] + return proc(*values) +# tag::EVAL_MATCH_BOTTOM[] + case _: # <5> + raise SyntaxError(lispstr(exp)) +# end::EVAL_MATCH_BOTTOM[] + +# tag::PROCEDURE[] +class Procedure: + "A user-defined Scheme procedure." + + def __init__( # <1> + self, parms: list[Symbol], body: list[Expression], env: Environment + ): + self.parms = parms # <2> + self.body = body + self.env = env + + def __call__(self, *args: Expression) -> Any: # <3> + local_env = dict(zip(self.parms, args)) # <4> + env = Environment(local_env, self.env) # <5> + for exp in self.body: # <6> + result = evaluate(exp, env) + return result # <7> +# end::PROCEDURE[] + + +################ command-line interface + +def run(source: str) -> Any: + global_env = standard_env() + tokens = tokenize(source) + while tokens: + exp = read_from_tokens(tokens) + result = evaluate(exp, global_env) + return result + +def main(args: list[str]) -> None: + if len(args) == 1: + with open(args[0]) as fp: + run(fp.read()) + else: + repl() + +if __name__ == '__main__': + import sys + main(sys.argv[1:]) diff --git a/02-array-seq/lispy/py3.10/lis_test.py b/02-array-seq/lispy/py3.10/lis_test.py new file mode 100644 index 0000000..03dc15c --- /dev/null +++ b/02-array-seq/lispy/py3.10/lis_test.py @@ -0,0 +1,180 @@ +from typing import Optional + +from pytest import mark, fixture + +from lis import parse, evaluate, Expression, Environment, standard_env + +############################################################# tests for parse + +@mark.parametrize( 'source, expected', [ + ('7', 7), + ('x', 'x'), + ('(sum 1 2 3)', ['sum', 1, 2, 3]), + ('(+ (* 2 100) (* 1 10))', ['+', ['*', 2, 100], ['*', 1, 10]]), + ('99 100', 99), # parse stops at the first complete expression + ('(a)(b)', ['a']), +]) +def test_parse(source: str, expected: Expression) -> None: + got = parse(source) + assert got == expected + + +########################################################## tests for evaluate + +# Norvig's tests are not isolated: they assume the +# same environment from first to last test. +global_env_for_first_test = standard_env() + +@mark.parametrize( 'source, expected', [ + ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), + ("(+ 2 2)", 4), + ("(+ (* 2 100) (* 1 10))", 210), + ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), + ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), + ("(define x 3)", None), + ("x", 3), + ("(+ x x)", 6), + ("((lambda (x) (+ x x)) 5)", 10), + ("(define twice (lambda (x) (* 2 x)))", None), + ("(twice 5)", 10), + ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), + ("((compose list twice) 5)", [10]), + ("(define repeat (lambda (f) (compose f f)))", None), + ("((repeat twice) 5)", 20), + ("((repeat (repeat twice)) 5)", 80), + ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), + ("(fact 3)", 6), + ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), + ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), + ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), + ("""(define combine (lambda (f) + (lambda (x y) + (if (null? x) (quote ()) + (f (list (car x) (car y)) + ((combine f) (cdr x) (cdr y)))))))""", None), + ("(define zip (combine cons))", None), + ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), + ("""(define riff-shuffle (lambda (deck) + (begin + (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) + (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) + (define mid (lambda (seq) (/ (length seq) 2))) + ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), + ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), + ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), + ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), +]) +def test_evaluate(source: str, expected: Optional[Expression]) -> None: + got = evaluate(parse(source), global_env_for_first_test) + assert got == expected + + +@fixture +def std_env() -> Environment: + return standard_env() + +# tests for cases in evaluate + +def test_evaluate_variable() -> None: + env = Environment({'x': 10}) + source = 'x' + expected = 10 + got = evaluate(parse(source), env) + assert got == expected + + +def test_evaluate_literal(std_env: Environment) -> None: + source = '3.3' + expected = 3.3 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_quote(std_env: Environment) -> None: + source = '(quote (1.1 is not 1))' + expected = [1.1, 'is', 'not', 1] + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_if_true(std_env: Environment) -> None: + source = '(if 1 10 no-such-thing)' + expected = 10 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_if_false(std_env: Environment) -> None: + source = '(if 0 no-such-thing 20)' + expected = 20 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_define(std_env: Environment) -> None: + source = '(define answer (* 6 7))' + got = evaluate(parse(source), std_env) + assert got is None + assert std_env['answer'] == 42 + + +def test_lambda(std_env: Environment) -> None: + source = '(lambda (a b) (if (>= a b) a b))' + func = evaluate(parse(source), std_env) + assert func.parms == ['a', 'b'] + assert func.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] + assert func.env is std_env + assert func(1, 2) == 2 + assert func(3, 2) == 3 + + +def test_begin(std_env: Environment) -> None: + source = """ + (begin + (define x (* 2 3)) + (* x 7) + ) + """ + got = evaluate(parse(source), std_env) + assert got == 42 + + +def test_invocation_builtin_car(std_env: Environment) -> None: + source = '(car (quote (11 22 33)))' + got = evaluate(parse(source), std_env) + assert got == 11 + + +def test_invocation_builtin_append(std_env: Environment) -> None: + source = '(append (quote (a b)) (quote (c d)))' + got = evaluate(parse(source), std_env) + assert got == ['a', 'b', 'c', 'd'] + + +def test_invocation_builtin_map(std_env: Environment) -> None: + source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))' + got = evaluate(parse(source), std_env) + assert got == [2, 4, 6] + + +def test_invocation_user_procedure(std_env: Environment) -> None: + source = """ + (begin + (define max (lambda (a b) (if (>= a b) a b))) + (max 22 11) + ) + """ + got = evaluate(parse(source), std_env) + assert got == 22 + + +def test_define_function(std_env: Environment) -> None: + source = '(define (max a b) (if (>= a b) a b))' + got = evaluate(parse(source), std_env) + assert got is None + max_fn = std_env['max'] + assert max_fn.parms == ['a', 'b'] + assert max_fn.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] + assert max_fn.env is std_env + assert max_fn(1, 2) == 2 + assert max_fn(3, 2) == 3 diff --git a/02-array-seq/lispy/py3.10/quicksort.scm b/02-array-seq/lispy/py3.10/quicksort.scm new file mode 100644 index 0000000..08dd596 --- /dev/null +++ b/02-array-seq/lispy/py3.10/quicksort.scm @@ -0,0 +1,17 @@ +(define (quicksort lst) + (if (null? lst) + lst + (begin + (define pivot (car lst)) + (define rest (cdr lst)) + (append + (quicksort + (filter (lambda (x) (< x pivot)) rest)) + (list pivot) + (quicksort + (filter (lambda (x) (>= x pivot)) rest))) + ) + ) +) +(display + (quicksort (list 2 1 6 3 4 0 8 9 7 5))) diff --git a/02-array-seq/lispy/py3.9/README.md b/02-array-seq/lispy/py3.9/README.md new file mode 100644 index 0000000..fdb1f08 --- /dev/null +++ b/02-array-seq/lispy/py3.9/README.md @@ -0,0 +1,33 @@ +# Changes from the original + +While adapting Peter Norvig's [lis.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lis.py) for +use in _Fluent Python, Second Edition_, I made a few changes for didactic reasons. + +_Luciano Ramalho_ + +## Major changes + +* Make the `lambda` form accept more than one expression as the body. This is consistent with [_Scheme_ syntax](https://web.mit.edu/scheme_v9.2/doc/mit-scheme-ref/Lambda-Expressions.html), and provides a useful example for the book. To implement this: + * In `Procedure.__call__`: evaluate `self.body` as a list of expressions, instead of a single expression. Return the value of the last expression. + * In `evaluate()`: when processing `lambda`, unpack expression into `(_, parms, *body)`, to accept a list of expressions as the body. +* Remove the `global_env` global `dict`. It is only used as a default value for the `env` parameter in `evaluate()`, but it is unsafe to use mutable data structures as parameter default values. To implement this: + * In `repl()`: create local variable `global_env` and pass it as the `env` paramater of `evaluate()`. + * In `evaluate()`, remove `global_env` default value for `env`. +* Rewrite the custom test script +[lispytest.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispytest.py) as +[lis_test.py](https://github.com/fluentpython/example-code-2e/blob/master/02-array-seq/lispy/py3.9/lis_test.py): +a standard [pytest](https://docs.pytest.org) test suite including new test cases, preserving all Norvig's test cases for +[lis.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lis.py) +but removing the test cases for the features implemented only in +[lispy.py](https://github.com/norvig/pytudes/blob/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispy.py). + + +## Minor changes + +Cosmetic changes to make the code look more familiar to +Python programmers, the audience of _Fluent Python_. + +* Rename `eval()` to `evaluate()`, to avoid confusion with Python's `eval` built-in function. +* Refer to the list class as `list` instead of aliasing as `List`, to avoid confusion with `typing.List` which is often imported as `List`. +* Import `collections.ChainMap` as `ChainMap` instead of `Environment`. + diff --git a/02-array-seq/lispy/py3.9/examples_test.py b/02-array-seq/lispy/py3.9/examples_test.py new file mode 100644 index 0000000..bdcda48 --- /dev/null +++ b/02-array-seq/lispy/py3.9/examples_test.py @@ -0,0 +1,196 @@ +""" +Doctests for `parse` +-------------------- + +# tag::PARSE[] +>>> from lis import parse +>>> parse('1.5') +1.5 +>>> parse('ni!') +'ni!' +>>> parse('(gcd 18 45)') +['gcd', 18, 45] +>>> parse(''' +... (define double +... (lambda (n) +... (* n 2))) +... ''') +['define', 'double', ['lambda', ['n'], ['*', 'n', 2]]] + +# end::PARSE[] + +Doctest for `Environment` +------------------------- + +# tag::ENVIRONMENT[] +>>> from lis import Environment +>>> inner_env = {'a': 2} +>>> outer_env = {'a': 0, 'b': 1} +>>> env = Environment(inner_env, outer_env) +>>> env['a'] = 111 # <1> +>>> env['c'] = 222 +>>> env +Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 1}) +>>> env.change('b', 333) # <2> +>>> env +Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 333}) + +# end::ENVIRONMENT[] + +Doctests for `evaluate` +----------------------- + +# tag::EVAL_NUMBER[] +>>> from lis import parse, evaluate, standard_env +>>> evaluate(parse('1.5'), {}) +1.5 + +# end::EVAL_NUMBER[] + +# tag::EVAL_SYMBOL[] +>>> from lis import standard_env +>>> evaluate(parse('+'), standard_env()) + +>>> evaluate(parse('ni!'), standard_env()) +Traceback (most recent call last): + ... +KeyError: 'ni!' + +# end::EVAL_SYMBOL[] + + +# tag::EVAL_QUOTE[] +>>> evaluate(parse('(quote no-such-name)'), standard_env()) +'no-such-name' +>>> evaluate(parse('(quote (99 bottles of beer))'), standard_env()) +[99, 'bottles', 'of', 'beer'] +>>> evaluate(parse('(quote (/ 10 0))'), standard_env()) +['/', 10, 0] + +# end::EVAL_QUOTE[] + +# tag::EVAL_IF[] +>>> evaluate(parse('(if (= 3 3) 1 0))'), standard_env()) +1 +>>> evaluate(parse('(if (= 3 4) 1 0))'), standard_env()) +0 + +# end::EVAL_IF[] + + +# tag::EVAL_LAMBDA[] +>>> expr = '(lambda (a b) (* (/ a b) 100))' +>>> f = evaluate(parse(expr), standard_env()) +>>> f # doctest: +ELLIPSIS + +>>> f(15, 20) +75.0 + +# end::EVAL_LAMBDA[] + +# tag::EVAL_DEFINE[] +>>> global_env = standard_env() +>>> evaluate(parse('(define answer (* 7 6))'), global_env) +>>> global_env['answer'] +42 + +# end::EVAL_DEFINE[] + +# tag::EVAL_DEFUN[] +>>> global_env = standard_env() +>>> percent = '(define % (lambda (a b) (* (/ a b) 100)))' +>>> evaluate(parse(percent), global_env) +>>> global_env['%'] # doctest: +ELLIPSIS + +>>> global_env['%'](170, 200) +85.0 + +# end::EVAL_DEFUN[] + +function call: + +# tag::EVAL_CALL[] +>>> evaluate(parse('(% (* 12 14) (- 500 100))'), global_env) +42.0 + +# end::EVAL_CALL[] + +""" + +import math + +from lis import run + + +fact_src = """ +(define ! + (lambda (n) + (if (< n 2) + 1 + (* n (! (- n 1))) + ) + ) +) +(! 42) +""" +def test_factorial(): + got = run(fact_src) + assert got == 1405006117752879898543142606244511569936384000000000 + assert got == math.factorial(42) + +closure_src = """ +(define make-adder + (lambda (increment) + (lambda (x) (+ increment x)) + ) +) +(define inc (make-adder 1)) +(inc 99) +""" +def test_closure(): + got = run(closure_src) + assert got == 100 + +closure_with_change_src = """ +(define make-counter + (lambda () + (define n 0) + (lambda () + (set! n (+ n 1)) + n) + ) +) +(define counter (make-counter)) +(display (counter)) +(display (counter)) +(display (counter)) +""" +def test_closure_with_change(capsys): + run(closure_with_change_src) + captured = capsys.readouterr() + assert captured.out == '1\n2\n3\n' + + + +# tag::RUN_AVERAGER[] +closure_averager_src = """ +(define make-averager + (lambda () + (define count 0) + (define total 0) + (lambda (new-value) + (set! count (+ count 1)) + (set! total (+ total new-value)) + (/ total count) + ) + ) +) +(define avg (make-averager)) +(avg 10) +(avg 11) +(avg 15) +""" +def test_closure_averager(): + got = run(closure_averager_src) + assert got == 12.0 +# end::RUN_AVERAGER[] \ No newline at end of file diff --git a/02-array-seq/lispy/py3.9/lis.py b/02-array-seq/lispy/py3.9/lis.py new file mode 100644 index 0000000..843d4cc --- /dev/null +++ b/02-array-seq/lispy/py3.9/lis.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python + +################ Lispy: Scheme Interpreter in Python 3.9 + +## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html +## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) +## by Luciano Ramalho, adding type hints and pattern matching. + + +################ Imports and Types + +import math +import operator as op +from collections import ChainMap +from itertools import chain +from typing import Any, Union, NoReturn + +Symbol = str +Atom = Union[float, int, Symbol] +Expression = Union[Atom, list] + + +################ Parsing: parse, tokenize, and read_from_tokens + +def parse(program: str) -> Expression: + "Read a Scheme expression from a string." + return read_from_tokens(tokenize(program)) + +def tokenize(s: str) -> list[str]: + "Convert a string into a list of tokens." + return s.replace('(', ' ( ').replace(')', ' ) ').split() + +def read_from_tokens(tokens: list[str]) -> Expression: + "Read an expression from a sequence of tokens." + if len(tokens) == 0: + raise SyntaxError('unexpected EOF while reading') + token = tokens.pop(0) + if '(' == token: + exp = [] + while tokens[0] != ')': + exp.append(read_from_tokens(tokens)) + tokens.pop(0) # discard ')' + return exp + elif ')' == token: + raise SyntaxError('unexpected )') + else: + return parse_atom(token) + +def parse_atom(token: str) -> Atom: + "Numbers become numbers; every other token is a symbol." + try: + return int(token) + except ValueError: + try: + return float(token) + except ValueError: + return Symbol(token) + + +################ Global Environment + +class Environment(ChainMap[Symbol, Any]): + "A ChainMap that allows changing an item in-place." + + def change(self, key: Symbol, value: object) -> None: + "Find where key is defined and change the value there." + for map in self.maps: + if key in map: + map[key] = value # type: ignore[index] + return + raise KeyError(key) + + +def standard_env() -> Environment: + "An environment with some Scheme standard procedures." + env = Environment() + env.update(vars(math)) # sin, cos, sqrt, pi, ... + env.update({ + '+': op.add, + '-': op.sub, + '*': op.mul, + '/': op.truediv, + '//': op.floordiv, + '>': op.gt, + '<': op.lt, + '>=': op.ge, + '<=': op.le, + '=': op.eq, + 'abs': abs, + 'append': lambda *args: list(chain(*args)), + 'apply': lambda proc, args: proc(*args), + 'begin': lambda *x: x[-1], + 'car': lambda x: x[0], + 'cdr': lambda x: x[1:], + 'cons': lambda x, y: [x] + y, + 'display': lambda x: print(lispstr(x)), + 'eq?': op.is_, + 'equal?': op.eq, + 'filter': lambda *args: list(filter(*args)), + 'length': len, + 'list': lambda *x: list(x), + 'list?': lambda x: isinstance(x, list), + 'map': lambda *args: list(map(*args)), + 'max': max, + 'min': min, + 'not': op.not_, + 'null?': lambda x: x == [], + 'number?': lambda x: isinstance(x, (int, float)), + 'procedure?': callable, + 'round': round, + 'symbol?': lambda x: isinstance(x, Symbol), + }) + return env + + +################ Interaction: A REPL + +def repl(prompt: str = 'lis.py> ') -> NoReturn: + "A prompt-read-eval-print loop." + global_env = standard_env() + while True: + ast = parse(input(prompt)) + val = evaluate(ast, global_env) + if val is not None: + print(lispstr(val)) + +def lispstr(exp: object) -> str: + "Convert a Python object back into a Lisp-readable string." + if isinstance(exp, list): + return '(' + ' '.join(map(lispstr, exp)) + ')' + else: + return str(exp) + + +################ Evaluator + +# tag::EVAL_IF_TOP[] +def evaluate(exp: Expression, env: Environment) -> Any: + "Evaluate an expression in an environment." + if isinstance(exp, Symbol): # variable reference + return env[exp] +# end::EVAL_IF_TOP[] + elif not isinstance(exp, list): # constant literal + return exp +# tag::EVAL_IF_MIDDLE[] + elif exp[0] == 'quote': # (quote exp) + (_, x) = exp + return x + elif exp[0] == 'if': # (if test conseq alt) + (_, test, consequence, alternative) = exp + if evaluate(test, env): + return evaluate(consequence, env) + else: + return evaluate(alternative, env) + elif exp[0] == 'lambda': # (lambda (parm…) body…) + (_, parms, *body) = exp + return Procedure(parms, body, env) + elif exp[0] == 'define': + (_, name, value_exp) = exp + env[name] = evaluate(value_exp, env) +# end::EVAL_IF_MIDDLE[] + elif exp[0] == 'set!': + (_, name, value_exp) = exp + env.change(name, evaluate(value_exp, env)) + else: # (proc arg…) + (func_exp, *args) = exp + proc = evaluate(func_exp, env) + args = [evaluate(arg, env) for arg in args] + return proc(*args) + + +class Procedure: + "A user-defined Scheme procedure." + + def __init__( + self, parms: list[Symbol], body: list[Expression], env: Environment + ): + self.parms = parms + self.body = body + self.env = env + + def __call__(self, *args: Expression) -> Any: + local_env = dict(zip(self.parms, args)) + env = Environment(local_env, self.env) + for exp in self.body: + result = evaluate(exp, env) + return result + + +################ command-line interface + +def run(source: str) -> Any: + global_env = standard_env() + tokens = tokenize(source) + while tokens: + exp = read_from_tokens(tokens) + result = evaluate(exp, global_env) + return result + +def main(args: list[str]) -> None: + if len(args) == 1: + with open(args[0]) as fp: + run(fp.read()) + else: + repl() + +if __name__ == '__main__': + import sys + main(sys.argv[1:]) diff --git a/02-array-seq/lispy/py3.9/lis_test.py b/02-array-seq/lispy/py3.9/lis_test.py new file mode 100644 index 0000000..64bafb4 --- /dev/null +++ b/02-array-seq/lispy/py3.9/lis_test.py @@ -0,0 +1,180 @@ +from typing import Optional + +from pytest import mark, fixture + +from lis import parse, evaluate, Expression, Environment, standard_env + +############################################################# tests for parse + +@mark.parametrize( 'source, expected', [ + ('7', 7), + ('x', 'x'), + ('(sum 1 2 3)', ['sum', 1, 2, 3]), + ('(+ (* 2 100) (* 1 10))', ['+', ['*', 2, 100], ['*', 1, 10]]), + ('99 100', 99), # parse stops at the first complete expression + ('(a)(b)', ['a']), +]) +def test_parse(source: str, expected: Expression) -> None: + got = parse(source) + assert got == expected + + +########################################################## tests for evaluate + +# Norvig's tests are not isolated: they assume the +# same environment from first to last test. +global_env_for_first_test = standard_env() + +@mark.parametrize( 'source, expected', [ + ("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]), + ("(+ 2 2)", 4), + ("(+ (* 2 100) (* 1 10))", 210), + ("(if (> 6 5) (+ 1 1) (+ 2 2))", 2), + ("(if (< 6 5) (+ 1 1) (+ 2 2))", 4), + ("(define x 3)", None), + ("x", 3), + ("(+ x x)", 6), + ("((lambda (x) (+ x x)) 5)", 10), + ("(define twice (lambda (x) (* 2 x)))", None), + ("(twice 5)", 10), + ("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None), + ("((compose list twice) 5)", [10]), + ("(define repeat (lambda (f) (compose f f)))", None), + ("((repeat twice) 5)", 20), + ("((repeat (repeat twice)) 5)", 80), + ("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None), + ("(fact 3)", 6), + ("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000), + ("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None), + ("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]), + ("""(define combine (lambda (f) + (lambda (x y) + (if (null? x) (quote ()) + (f (list (car x) (car y)) + ((combine f) (cdr x) (cdr y)))))))""", None), + ("(define zip (combine cons))", None), + ("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]), + ("""(define riff-shuffle (lambda (deck) + (begin + (define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq)))))) + (define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq))))) + (define mid (lambda (seq) (/ (length seq) 2))) + ((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None), + ("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]), + ("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]), + ("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]), +]) +def test_evaluate(source: str, expected: Optional[Expression]) -> None: + got = evaluate(parse(source), global_env_for_first_test) + assert got == expected + + +@fixture +def std_env() -> Environment: + return standard_env() + +# tests for cases in evaluate + +def test_evaluate_variable() -> None: + env = Environment({'x': 10}) + source = 'x' + expected = 10 + got = evaluate(parse(source), env) + assert got == expected + + +def test_evaluate_literal(std_env: Environment) -> None: + source = '3.3' + expected = 3.3 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_quote(std_env: Environment) -> None: + source = '(quote (1.1 is not 1))' + expected = [1.1, 'is', 'not', 1] + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_if_true(std_env: Environment) -> None: + source = '(if 1 10 no-such-thing)' + expected = 10 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_evaluate_if_false(std_env: Environment) -> None: + source = '(if 0 no-such-thing 20)' + expected = 20 + got = evaluate(parse(source), std_env) + assert got == expected + + +def test_define(std_env: Environment) -> None: + source = '(define answer (* 6 7))' + got = evaluate(parse(source), std_env) + assert got is None + assert std_env['answer'] == 42 + + +def test_lambda(std_env: Environment) -> None: + source = '(lambda (a b) (if (>= a b) a b))' + func = evaluate(parse(source), std_env) + assert func.parms == ['a', 'b'] + assert func.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] + assert func.env is std_env + assert func(1, 2) == 2 + assert func(3, 2) == 3 + + +def test_begin(std_env: Environment) -> None: + source = """ + (begin + (define x (* 2 3)) + (* x 7) + ) + """ + got = evaluate(parse(source), std_env) + assert got == 42 + + +def test_invocation_builtin_car(std_env: Environment) -> None: + source = '(car (quote (11 22 33)))' + got = evaluate(parse(source), std_env) + assert got == 11 + + +def test_invocation_builtin_append(std_env: Environment) -> None: + source = '(append (quote (a b)) (quote (c d)))' + got = evaluate(parse(source), std_env) + assert got == ['a', 'b', 'c', 'd'] + + +def test_invocation_builtin_map(std_env: Environment) -> None: + source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))' + got = evaluate(parse(source), std_env) + assert got == [2, 4, 6] + + +def test_invocation_user_procedure(std_env: Environment) -> None: + source = """ + (begin + (define max (lambda (a b) (if (>= a b) a b))) + (max 22 11) + ) + """ + got = evaluate(parse(source), std_env) + assert got == 22 + + +def test_define_function(std_env: Environment) -> None: + source = '(define max (lambda (a b) (if (>= a b) a b)))' + got = evaluate(parse(source), std_env) + assert got is None + max_fn = std_env['max'] + assert max_fn.parms == ['a', 'b'] + assert max_fn.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] + assert max_fn.env is std_env + assert max_fn(1, 2) == 2 + assert max_fn(3, 2) == 3 diff --git a/18-with-match/lispy/py3.10/examples_test.py b/18-with-match/lispy/py3.10/examples_test.py index ed4b68a..ba6bd10 100644 --- a/18-with-match/lispy/py3.10/examples_test.py +++ b/18-with-match/lispy/py3.10/examples_test.py @@ -2,16 +2,12 @@ Doctests for `parse` -------------------- -# tag::PARSE_ATOM[] +# tag::PARSE[] >>> from lis import parse >>> parse('1.5') 1.5 >>> parse('ni!') 'ni!' - -# end::PARSE_ATOM[] - -# tag::PARSE_LIST[] >>> parse('(gcd 18 45)') ['gcd', 18, 45] >>> parse(''' @@ -21,15 +17,15 @@ ... ''') ['define', 'double', ['lambda', ['n'], ['*', 'n', 2]]] -# end::PARSE_LIST[] +# end::PARSE[] Doctest for `Environment` ------------------------- # tag::ENVIRONMENT[] >>> from lis import Environment ->>> outer_env = {'a': 0, 'b': 1} >>> inner_env = {'a': 2} +>>> outer_env = {'a': 0, 'b': 1} >>> env = Environment(inner_env, outer_env) >>> env['a'] = 111 # <1> >>> env['c'] = 222 @@ -64,11 +60,11 @@ # tag::EVAL_QUOTE[] ->>> evaluate(parse('(quote no-such-name)'), {}) +>>> evaluate(parse('(quote no-such-name)'), standard_env()) 'no-such-name' ->>> evaluate(parse('(quote (99 bottles of beer))'), {}) +>>> evaluate(parse('(quote (99 bottles of beer))'), standard_env()) [99, 'bottles', 'of', 'beer'] ->>> evaluate(parse('(quote (/ 10 0))'), {}) +>>> evaluate(parse('(quote (/ 10 0))'), standard_env()) ['/', 10, 0] # end::EVAL_QUOTE[] @@ -156,11 +152,12 @@ def test_factorial(): (if (= n 0) m (gcd n (mod m n)))) -(gcd 18 45) +(display (gcd 18 45)) """ -def test_gcd(): - got = run(gcd_src) - assert got == 9 +def test_gcd(capsys): + run(gcd_src) + captured = capsys.readouterr() + assert captured.out == '9\n' quicksort_src = """ @@ -216,7 +213,7 @@ def test_newton(): (define inc (make-adder 1)) (inc 99) """ -def test_newton(): +def test_closure(): got = run(closure_src) assert got == 100 @@ -228,13 +225,15 @@ def test_newton(): n) ) (define counter (make-counter)) -(counter) -(counter) -(counter) +(display (counter)) +(display (counter)) +(display (counter)) """ -def test_closure_with_change(): - got = run(closure_with_change_src) - assert got == 3 +def test_closure_with_change(capsys): + run(closure_with_change_src) + captured = capsys.readouterr() + assert captured.out == '1\n2\n3\n' + # tag::RUN_AVERAGER[] @@ -256,4 +255,4 @@ def test_closure_with_change(): def test_closure_averager(): got = run(closure_averager_src) assert got == 12.0 -# end::RUN_AVERAGER[] \ No newline at end of file +# end::RUN_AVERAGER[] diff --git a/18-with-match/lispy/py3.10/lis.py b/18-with-match/lispy/py3.10/lis.py index 4a5195c..e05ee9c 100755 --- a/18-with-match/lispy/py3.10/lis.py +++ b/18-with-match/lispy/py3.10/lis.py @@ -58,10 +58,11 @@ def parse_atom(token: str) -> Atom: except ValueError: return Symbol(token) + ################ Global Environment # tag::ENV_CLASS[] -class Environment(ChainMap): +class Environment(ChainMap[Symbol, Any]): "A ChainMap that allows changing an item in-place." def change(self, key: Symbol, value: object) -> None: @@ -73,7 +74,6 @@ def change(self, key: Symbol, value: object) -> None: raise KeyError(key) # end::ENV_CLASS[] - def standard_env() -> Environment: "An environment with some Scheme standard procedures." env = Environment() @@ -119,11 +119,11 @@ def standard_env() -> Environment: ################ Interaction: A REPL # tag::REPL[] -def repl() -> NoReturn: +def repl(prompt: str = 'lis.py> ') -> NoReturn: "A prompt-read-eval-print loop." global_env = standard_env() while True: - ast = parse(input('lis.py> ')) + ast = parse(input(prompt)) val = evaluate(ast, global_env) if val is not None: print(lispstr(val)) @@ -149,8 +149,6 @@ def evaluate(exp: Expression, env: Environment) -> Any: return x case Symbol(var): return env[var] - case []: - return [] case ['quote', x]: return x case ['if', test, consequence, alternative]: @@ -166,8 +164,8 @@ def evaluate(exp: Expression, env: Environment) -> Any: env[name] = Procedure(parms, body, env) case ['set!', Symbol(var), value_exp]: env.change(var, evaluate(value_exp, env)) - case [op, *args] if op not in KEYWORDS: - proc = evaluate(op, env) + case [func_exp, *args] if func_exp not in KEYWORDS: + proc = evaluate(func_exp, env) values = [evaluate(arg, env) for arg in args] return proc(*values) case _: diff --git a/18-with-match/lispy/py3.10/lis_test.py b/18-with-match/lispy/py3.10/lis_test.py index 3688888..03dc15c 100644 --- a/18-with-match/lispy/py3.10/lis_test.py +++ b/18-with-match/lispy/py3.10/lis_test.py @@ -73,10 +73,10 @@ def test_evaluate(source: str, expected: Optional[Expression]) -> None: def std_env() -> Environment: return standard_env() -# tests for each of the cases in evaluate +# tests for cases in evaluate def test_evaluate_variable() -> None: - env: Environment = dict(x=10) + env = Environment({'x': 10}) source = 'x' expected = 10 got = evaluate(parse(source), env) @@ -168,8 +168,6 @@ def test_invocation_user_procedure(std_env: Environment) -> None: assert got == 22 -###################################### for py3.10/lis.py only - def test_define_function(std_env: Environment) -> None: source = '(define (max a b) (if (>= a b) a b))' got = evaluate(parse(source), std_env) diff --git a/18-with-match/lispy/py3.9/examples_test.py b/18-with-match/lispy/py3.9/examples_test.py new file mode 100644 index 0000000..c632662 --- /dev/null +++ b/18-with-match/lispy/py3.9/examples_test.py @@ -0,0 +1,258 @@ +""" +Doctests for `parse` +-------------------- + +# tag::PARSE[] +>>> from lis import parse +>>> parse('1.5') +1.5 +>>> parse('ni!') +'ni!' +>>> parse('(gcd 18 45)') +['gcd', 18, 45] +>>> parse(''' +... (define double +... (lambda (n) +... (* n 2))) +... ''') +['define', 'double', ['lambda', ['n'], ['*', 'n', 2]]] + +# end::PARSE[] + +Doctest for `Environment` +------------------------- + +# tag::ENVIRONMENT[] +>>> from lis import Environment +>>> inner_env = {'a': 2} +>>> outer_env = {'a': 0, 'b': 1} +>>> env = Environment(inner_env, outer_env) +>>> env['a'] = 111 # <1> +>>> env['c'] = 222 +>>> env +Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 1}) +>>> env.change('b', 333) # <2> +>>> env +Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 333}) + +# end::ENVIRONMENT[] + +Doctests for `evaluate` +----------------------- + +# tag::EVAL_NUMBER[] +>>> from lis import parse, evaluate, standard_env +>>> evaluate(parse('1.5'), {}) +1.5 + +# end::EVAL_NUMBER[] + +# tag::EVAL_SYMBOL[] +>>> from lis import standard_env +>>> evaluate(parse('+'), standard_env()) + +>>> evaluate(parse('ni!'), standard_env()) +Traceback (most recent call last): + ... +KeyError: 'ni!' + +# end::EVAL_SYMBOL[] + + +# tag::EVAL_QUOTE[] +>>> evaluate(parse('(quote no-such-name)'), standard_env()) +'no-such-name' +>>> evaluate(parse('(quote (99 bottles of beer))'), standard_env()) +[99, 'bottles', 'of', 'beer'] +>>> evaluate(parse('(quote (/ 10 0))'), standard_env()) +['/', 10, 0] + +# end::EVAL_QUOTE[] + +# tag::EVAL_IF[] +>>> evaluate(parse('(if (= 3 3) 1 0))'), standard_env()) +1 +>>> evaluate(parse('(if (= 3 4) 1 0))'), standard_env()) +0 + +# end::EVAL_IF[] + + +# tag::EVAL_LAMBDA[] +>>> expr = '(lambda (a b) (* (/ a b) 100))' +>>> f = evaluate(parse(expr), standard_env()) +>>> f # doctest: +ELLIPSIS + +>>> f(15, 20) +75.0 + +# end::EVAL_LAMBDA[] + +# tag::EVAL_DEFINE[] +>>> global_env = standard_env() +>>> evaluate(parse('(define answer (* 7 6))'), global_env) +>>> global_env['answer'] +42 + +# end::EVAL_DEFINE[] + +# tag::EVAL_DEFUN[] +>>> global_env = standard_env() +>>> percent = '(define (% a b) (* (/ a b) 100))' +>>> evaluate(parse(percent), global_env) +>>> global_env['%'] # doctest: +ELLIPSIS + +>>> global_env['%'](170, 200) +85.0 + +# end::EVAL_DEFUN[] + +function call: + +# tag::EVAL_CALL[] +>>> evaluate(parse('(% (* 12 14) (- 500 100))'), global_env) +42.0 + +# end::EVAL_CALL[] + +# tag::EVAL_SYNTAX_ERROR[] +>>> evaluate(parse('(lambda is not like this)'), standard_env()) +Traceback (most recent call last): + ... +SyntaxError: (lambda is not like this) + +# end::EVAL_SYNTAX_ERROR[] + +""" + +import math + +from lis import run + + +fact_src = """ +(define (! n) + (if (< n 2) + 1 + (* n (! (- n 1))) + ) +) +(! 42) +""" +def test_factorial(): + got = run(fact_src) + assert got == 1405006117752879898543142606244511569936384000000000 + assert got == math.factorial(42) + + +gcd_src = """ +(define (mod m n) + (- m (* n (// m n)))) +(define (gcd m n) + (if (= n 0) + m + (gcd n (mod m n)))) +(display (gcd 18 45)) +""" +def test_gcd(capsys): + run(gcd_src) + captured = capsys.readouterr() + assert captured.out == '9\n' + + +quicksort_src = """ +(define (quicksort lst) + (if (null? lst) + lst + (begin + (define pivot (car lst)) + (define rest (cdr lst)) + (append + (quicksort + (filter (lambda (x) (< x pivot)) rest)) + (list pivot) + (quicksort + (filter (lambda (x) (>= x pivot)) rest))) + ) + ) +) +(quicksort (list 2 1 6 3 4 0 8 9 7 5)) +""" +def test_quicksort(): + got = run(quicksort_src) + assert got == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + +# Example from Structure and Interpretation of Computer Programs +# https://mitpress.mit.edu/sites/default/files/sicp/full-text/sicp/book/node12.html + +newton_src = """ +(define (sqrt x) + (sqrt-iter 1.0 x)) +(define (sqrt-iter guess x) + (if (good-enough? guess x) + guess + (sqrt-iter (improve guess x) x))) +(define (good-enough? guess x) + (< (abs (- (* guess guess) x)) 0.001)) +(define (improve guess x) + (average guess (/ x guess))) +(define (average x y) + (/ (+ x y) 2)) +(sqrt 123454321) +""" +def test_newton(): + got = run(newton_src) + assert math.isclose(got, 11111) + + +closure_src = """ +(define (make-adder increment) + (lambda (x) (+ increment x)) +) +(define inc (make-adder 1)) +(inc 99) +""" +def test_closure(): + got = run(closure_src) + assert got == 100 + +closure_with_change_src = """ +(define (make-counter) + (define n 0) + (lambda () + (set! n (+ n 1)) + n) +) +(define counter (make-counter)) +(display (counter)) +(display (counter)) +(display (counter)) +""" +def test_closure_with_change(capsys): + run(closure_with_change_src) + captured = capsys.readouterr() + assert captured.out == '1\n2\n3\n' + + + +# tag::RUN_AVERAGER[] +closure_averager_src = """ +(define (make-averager) + (define count 0) + (define total 0) + (lambda (new-value) + (set! count (+ count 1)) + (set! total (+ total new-value)) + (/ total count) + ) +) +(define avg (make-averager)) +(avg 10) +(avg 11) +(avg 15) +""" +def test_closure_averager(): + got = run(closure_averager_src) + assert got == 12.0 +# end::RUN_AVERAGER[] \ No newline at end of file diff --git a/18-with-match/lispy/py3.9/lis.py b/18-with-match/lispy/py3.9/lis.py index 9e4dec1..e8881cd 100644 --- a/18-with-match/lispy/py3.9/lis.py +++ b/18-with-match/lispy/py3.9/lis.py @@ -1,73 +1,24 @@ +#!/usr/bin/env python + ################ Lispy: Scheme Interpreter in Python 3.9 ## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html ## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021) ## by Luciano Ramalho, adding type hints and pattern matching. + ################ Imports and Types import math import operator as op from collections import ChainMap -from collections.abc import MutableMapping, Iterator from itertools import chain -from typing import Any, Union +from typing import Any, Union, NoReturn Symbol = str Atom = Union[float, int, Symbol] Expression = Union[Atom, list] -Environment = MutableMapping[Symbol, object] - - -class Procedure: - "A user-defined Scheme procedure." - - def __init__(self, parms: list[Symbol], body: list[Expression], env: Environment): - self.parms = parms - self.body = body - self.env = env - - def __call__(self, *args: Expression) -> Any: - local_env = dict(zip(self.parms, args)) - env: Environment = ChainMap(local_env, self.env) - for exp in self.body: - result = evaluate(exp, env) - return result - - -################ Global Environment - -def standard_env() -> Environment: - "An environment with some Scheme standard procedures." - env: Environment = {} - env.update(vars(math)) # sin, cos, sqrt, pi, ... - env.update({ - '+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, - '>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq, - 'abs': abs, - 'append': op.add, - 'apply': lambda proc, args: proc(*args), - 'begin': lambda *x: x[-1], - 'car': lambda x: x[0], - 'cdr': lambda x: x[1:], - 'cons': lambda x,y: [x] + y, - 'eq?': op.is_, - 'equal?': op.eq, - 'length': len, - 'list': lambda *x: list(x), - 'list?': lambda x: isinstance(x,list), - 'map': lambda *args: list(map(*args)), - 'max': max, - 'min': min, - 'not': op.not_, - 'null?': lambda x: x == [], - 'number?': lambda x: isinstance(x, (int, float)), - 'procedure?': callable, - 'round': round, - 'symbol?': lambda x: isinstance(x, Symbol), - }) - return env ################ Parsing: parse, tokenize, and read_from_tokens @@ -75,12 +26,10 @@ def parse(program: str) -> Expression: "Read a Scheme expression from a string." return read_from_tokens(tokenize(program)) - def tokenize(s: str) -> list[str]: "Convert a string into a list of tokens." return s.replace('(', ' ( ').replace(')', ' ) ').split() - def read_from_tokens(tokens: list[str]) -> Expression: "Read an expression from a sequence of tokens." if len(tokens) == 0: @@ -97,7 +46,6 @@ def read_from_tokens(tokens: list[str]) -> Expression: else: return parse_atom(token) - def parse_atom(token: str) -> Atom: "Numbers become numbers; every other token is a symbol." try: @@ -109,17 +57,73 @@ def parse_atom(token: str) -> Atom: return Symbol(token) +################ Global Environment + +class Environment(ChainMap[Symbol, Any]): + "A ChainMap that allows changing an item in-place." + + def change(self, key: Symbol, value: object) -> None: + "Find where key is defined and change the value there." + for map in self.maps: + if key in map: + map[key] = value # type: ignore[index] + return + raise KeyError(key) + + +def standard_env() -> Environment: + "An environment with some Scheme standard procedures." + env = Environment() + env.update(vars(math)) # sin, cos, sqrt, pi, ... + env.update({ + '+': op.add, + '-': op.sub, + '*': op.mul, + '/': op.truediv, + '//': op.floordiv, + '>': op.gt, + '<': op.lt, + '>=': op.ge, + '<=': op.le, + '=': op.eq, + 'abs': abs, + 'append': lambda *args: list(chain(*args)), + 'apply': lambda proc, args: proc(*args), + 'begin': lambda *x: x[-1], + 'car': lambda x: x[0], + 'cdr': lambda x: x[1:], + 'cons': lambda x, y: [x] + y, + 'display': lambda x: print(lispstr(x)), + 'eq?': op.is_, + 'equal?': op.eq, + 'filter': lambda *args: list(filter(*args)), + 'length': len, + 'list': lambda *x: list(x), + 'list?': lambda x: isinstance(x, list), + 'map': lambda *args: list(map(*args)), + 'max': max, + 'min': min, + 'not': op.not_, + 'null?': lambda x: x == [], + 'number?': lambda x: isinstance(x, (int, float)), + 'procedure?': callable, + 'round': round, + 'symbol?': lambda x: isinstance(x, Symbol), + }) + return env + + ################ Interaction: A REPL -def repl(prompt: str = 'lis.py> ') -> None: +def repl(prompt: str = 'lis.py> ') -> NoReturn: "A prompt-read-eval-print loop." global_env = standard_env() while True: - val = evaluate(parse(input(prompt)), global_env) + ast = parse(input(prompt)) + val = evaluate(ast, global_env) if val is not None: print(lispstr(val)) - def lispstr(exp: object) -> str: "Convert a Python object back into a Lisp-readable string." if isinstance(exp, list): @@ -128,30 +132,81 @@ def lispstr(exp: object) -> str: return str(exp) -################ eval +################ Evaluator -def evaluate(x: Expression, env: Environment) -> Any: +def evaluate(exp: Expression, env: Environment) -> Any: "Evaluate an expression in an environment." - if isinstance(x, Symbol): # variable reference - return env[x] - elif not isinstance(x, list): # constant literal - return x - elif x[0] == 'quote': # (quote exp) - (_, exp) = x + if isinstance(exp, Symbol): # variable reference + return env[exp] + elif not isinstance(exp, list): # constant literal return exp - elif x[0] == 'if': # (if test conseq alt) - (_, test, consequence, alternative) = x + elif exp[0] == 'quote': # (quote exp) + (_, x) = exp + return x + elif exp[0] == 'if': # (if test conseq alt) + (_, test, consequence, alternative) = exp if evaluate(test, env): return evaluate(consequence, env) else: return evaluate(alternative, env) - elif x[0] == 'define': # (define name exp) - (_, name, exp) = x - env[name] = evaluate(exp, env) - elif x[0] == 'lambda': # (lambda (parm…) body) - (_, parms, *body) = x + elif exp[0] == 'lambda': # (lambda (parm…) body…) + (_, parms, *body) = exp + if not isinstance(parms, list): + raise SyntaxError(lispstr(exp)) return Procedure(parms, body, env) + elif exp[0] == 'define': + (_, name_exp, *rest) = exp + if isinstance(name_exp, Symbol): # (define name exp) + value_exp = rest[0] + env[name_exp] = evaluate(value_exp, env) + else: # (define (name parm…) body…) + name, *parms = name_exp + env[name] = Procedure(parms, rest, env) + elif exp[0] == 'set!': + (_, var, value_exp) = exp + env.change(var, evaluate(value_exp, env)) else: # (proc arg…) - proc = evaluate(x[0], env) - args = [evaluate(exp, env) for exp in x[1:]] + (func_exp, *args) = exp + proc = evaluate(func_exp, env) + args = [evaluate(arg, env) for arg in args] return proc(*args) + + +class Procedure: + "A user-defined Scheme procedure." + + def __init__( + self, parms: list[Symbol], body: list[Expression], env: Environment + ): + self.parms = parms + self.body = body + self.env = env + + def __call__(self, *args: Expression) -> Any: + local_env = dict(zip(self.parms, args)) + env = Environment(local_env, self.env) + for exp in self.body: + result = evaluate(exp, env) + return result + + +################ command-line interface + +def run(source: str) -> Any: + global_env = standard_env() + tokens = tokenize(source) + while tokens: + exp = read_from_tokens(tokens) + result = evaluate(exp, global_env) + return result + +def main(args: list[str]) -> None: + if len(args) == 1: + with open(args[0]) as fp: + run(fp.read()) + else: + repl() + +if __name__ == '__main__': + import sys + main(sys.argv[1:]) diff --git a/18-with-match/lispy/py3.9/lis_test.py b/18-with-match/lispy/py3.9/lis_test.py index 106ac24..03dc15c 100644 --- a/18-with-match/lispy/py3.9/lis_test.py +++ b/18-with-match/lispy/py3.9/lis_test.py @@ -1,8 +1,8 @@ -from typing import Any, Optional +from typing import Optional from pytest import mark, fixture -from lis import parse, evaluate, standard_env, Symbol, Environment, Expression +from lis import parse, evaluate, Expression, Environment, standard_env ############################################################# tests for parse @@ -73,10 +73,10 @@ def test_evaluate(source: str, expected: Optional[Expression]) -> None: def std_env() -> Environment: return standard_env() -# tests for each of the cases in evaluate +# tests for cases in evaluate def test_evaluate_variable() -> None: - env: Environment = dict(x=10) + env = Environment({'x': 10}) source = 'x' expected = 10 got = evaluate(parse(source), env) @@ -166,3 +166,15 @@ def test_invocation_user_procedure(std_env: Environment) -> None: """ got = evaluate(parse(source), std_env) assert got == 22 + + +def test_define_function(std_env: Environment) -> None: + source = '(define (max a b) (if (>= a b) a b))' + got = evaluate(parse(source), std_env) + assert got is None + max_fn = std_env['max'] + assert max_fn.parms == ['a', 'b'] + assert max_fn.body == [['if', ['>=', 'a', 'b'], 'a', 'b']] + assert max_fn.env is std_env + assert max_fn(1, 2) == 2 + assert max_fn(3, 2) == 3 diff --git a/18-with-match/mirror_gen.py b/18-with-match/mirror_gen.py index 6dfaf02..2758045 100644 --- a/18-with-match/mirror_gen.py +++ b/18-with-match/mirror_gen.py @@ -15,6 +15,9 @@ YKCOWREBBAJ >>> what 'JABBERWOCKY' + >>> print('back to normal') + back to normal + # end::MIRROR_GEN_DEMO_1[] diff --git a/22-dyn-attr-prop/doc_property.py b/22-dyn-attr-prop/doc_property.py index b1ba351..5b653ef 100644 --- a/22-dyn-attr-prop/doc_property.py +++ b/22-dyn-attr-prop/doc_property.py @@ -14,7 +14,7 @@ class Foo: @property def bar(self): - '''The bar attribute''' + """The bar attribute""" return self.__dict__['bar'] @bar.setter From ade9577a55f1337ee5ca2b85d40f8eafc161d648 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 20 Sep 2021 23:31:45 -0300 Subject: [PATCH 070/127] sync from Atlas --- 02-array-seq/lispy/py3.10/lis.py | 4 ++-- 02-array-seq/lispy/py3.9/lis.py | 4 ++-- 18-with-match/lispy/py3.10/examples_test.py | 1 - 18-with-match/lispy/py3.10/lis.py | 21 +++++++------------ 18-with-match/lispy/py3.9/lis.py | 4 ++-- .../primes/spinner_prime_async_nap.py | 15 ++++++------- 6 files changed, 21 insertions(+), 28 deletions(-) diff --git a/02-array-seq/lispy/py3.10/lis.py b/02-array-seq/lispy/py3.10/lis.py index cffb9a1..150fca7 100755 --- a/02-array-seq/lispy/py3.10/lis.py +++ b/02-array-seq/lispy/py3.10/lis.py @@ -122,7 +122,7 @@ def standard_env() -> Environment: # tag::REPL[] def repl(prompt: str = 'lis.py> ') -> NoReturn: "A prompt-read-eval-print loop." - global_env = standard_env() + global_env = Environment({}, standard_env()) while True: ast = parse(input(prompt)) val = evaluate(ast, global_env) @@ -200,7 +200,7 @@ def __call__(self, *args: Expression) -> Any: # <3> ################ command-line interface def run(source: str) -> Any: - global_env = standard_env() + global_env = Environment({}, standard_env()) tokens = tokenize(source) while tokens: exp = read_from_tokens(tokens) diff --git a/02-array-seq/lispy/py3.9/lis.py b/02-array-seq/lispy/py3.9/lis.py index 843d4cc..201b41e 100644 --- a/02-array-seq/lispy/py3.9/lis.py +++ b/02-array-seq/lispy/py3.9/lis.py @@ -117,7 +117,7 @@ def standard_env() -> Environment: def repl(prompt: str = 'lis.py> ') -> NoReturn: "A prompt-read-eval-print loop." - global_env = standard_env() + global_env = Environment({}, standard_env()) while True: ast = parse(input(prompt)) val = evaluate(ast, global_env) @@ -190,7 +190,7 @@ def __call__(self, *args: Expression) -> Any: ################ command-line interface def run(source: str) -> Any: - global_env = standard_env() + global_env = Environment({}, standard_env()) tokens = tokenize(source) while tokens: exp = read_from_tokens(tokens) diff --git a/18-with-match/lispy/py3.10/examples_test.py b/18-with-match/lispy/py3.10/examples_test.py index ba6bd10..2807596 100644 --- a/18-with-match/lispy/py3.10/examples_test.py +++ b/18-with-match/lispy/py3.10/examples_test.py @@ -48,7 +48,6 @@ # end::EVAL_NUMBER[] # tag::EVAL_SYMBOL[] ->>> from lis import standard_env >>> evaluate(parse('+'), standard_env()) >>> evaluate(parse('ni!'), standard_env()) diff --git a/18-with-match/lispy/py3.10/lis.py b/18-with-match/lispy/py3.10/lis.py index 0feeadc..30ad8a8 100755 --- a/18-with-match/lispy/py3.10/lis.py +++ b/18-with-match/lispy/py3.10/lis.py @@ -121,7 +121,7 @@ def standard_env() -> Environment: # tag::REPL[] def repl(prompt: str = 'lis.py> ') -> NoReturn: "A prompt-read-eval-print loop." - global_env = standard_env() + global_env = Environment({}, standard_env()) while True: ast = parse(input(prompt)) val = evaluate(ast, global_env) @@ -140,10 +140,7 @@ def lispstr(exp: object) -> str: ################ Evaluator # tag::EVALUATE[] -KEYWORDS = {'quote', 'if', 'lambda', 'define', 'set!'} - -def is_keyword(s: Any) -> bool: - return isinstance(s, Symbol) and s in KEYWORDS +KEYWORDS = ['quote', 'if', 'lambda', 'define', 'set!'] def evaluate(exp: Expression, env: Environment) -> Any: "Evaluate an expression in an environment." @@ -161,17 +158,13 @@ def evaluate(exp: Expression, env: Environment) -> Any: return evaluate(alternative, env) case ['lambda', [*parms], *body] if body: return Procedure(parms, body, env) - case ['define', Symbol(var), value_exp]: - env[var] = evaluate(value_exp, env) + case ['define', Symbol(name), value_exp]: + env[name] = evaluate(value_exp, env) case ['define', [Symbol(name), *parms], *body] if body: env[name] = Procedure(parms, body, env) - case ['set!', Symbol(var), value_exp]: - env.change(var, evaluate(value_exp, env)) -<<<<<<< HEAD + case ['set!', Symbol(name), value_exp]: + env.change(name, evaluate(value_exp, env)) case [func_exp, *args] if func_exp not in KEYWORDS: -======= - case [func_exp, *args] if not is_keyword(func_exp): ->>>>>>> 3ecfb212c6273122797c76876d6b373b2cb94fa6 proc = evaluate(func_exp, env) values = [evaluate(arg, env) for arg in args] return proc(*values) @@ -202,7 +195,7 @@ def __call__(self, *args: Expression) -> Any: # <3> ################ command-line interface def run(source: str) -> Any: - global_env = standard_env() + global_env = Environment({}, standard_env()) tokens = tokenize(source) while tokens: exp = read_from_tokens(tokens) diff --git a/18-with-match/lispy/py3.9/lis.py b/18-with-match/lispy/py3.9/lis.py index e8881cd..6ab8a19 100644 --- a/18-with-match/lispy/py3.9/lis.py +++ b/18-with-match/lispy/py3.9/lis.py @@ -117,7 +117,7 @@ def standard_env() -> Environment: def repl(prompt: str = 'lis.py> ') -> NoReturn: "A prompt-read-eval-print loop." - global_env = standard_env() + global_env = Environment({}, standard_env()) while True: ast = parse(input(prompt)) val = evaluate(ast, global_env) @@ -193,7 +193,7 @@ def __call__(self, *args: Expression) -> Any: ################ command-line interface def run(source: str) -> Any: - global_env = standard_env() + global_env = Environment({}, standard_env()) tokens = tokenize(source) while tokens: exp = read_from_tokens(tokens) diff --git a/19-concurrency/primes/spinner_prime_async_nap.py b/19-concurrency/primes/spinner_prime_async_nap.py index 4d6fa47..b6e6518 100644 --- a/19-concurrency/primes/spinner_prime_async_nap.py +++ b/19-concurrency/primes/spinner_prime_async_nap.py @@ -7,6 +7,7 @@ import asyncio import itertools import math +import functools # tag::PRIME_NAP[] async def is_prime(n): @@ -21,8 +22,8 @@ async def is_prime(n): for i in range(3, root + 1, 2): if n % i == 0: return False - if i % 100_000 == 1: # <2> - await asyncio.sleep(0) + if i % 100_000 == 1: + await asyncio.sleep(0) # <1> return True # end::PRIME_NAP[] @@ -39,13 +40,13 @@ async def spin(msg: str) -> None: print(f'\r{blanks}\r', end='') async def check(n: int) -> int: - return await is_prime(n) # <4> + return await is_prime(n) async def supervisor(n: int) -> int: - spinner = asyncio.create_task(spin('thinking!')) # <1> - print('spinner object:', spinner) # <2> - result = await check(n) # <3> - spinner.cancel() # <5> + spinner = asyncio.create_task(spin('thinking!')) + print('spinner object:', spinner) + result = await check(n) + spinner.cancel() return result def main() -> None: From 2c1230579db99738a5e5e6802063bda585f6476d Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Tue, 21 Sep 2021 18:03:34 -0300 Subject: [PATCH 071/127] ch19: fixed race condition in procs.py --- 19-concurrency/primes/procs.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/19-concurrency/primes/procs.py b/19-concurrency/primes/procs.py index fc84049..8624958 100644 --- a/19-concurrency/primes/procs.py +++ b/19-concurrency/primes/procs.py @@ -30,6 +30,7 @@ def check(n: int) -> PrimeResult: # <6> def worker(jobs: JobQueue, results: ResultQueue) -> None: # <7> while n := jobs.get(): # <8> results.put(check(n)) # <9> + results.put(PrimeResult(0, False, 0.0)) # <10> # end::PRIMES_PROC_TOP[] # tag::PRIMES_PROC_MAIN[] @@ -53,15 +54,19 @@ def main() -> None: proc.start() # <5> jobs.put(0) # <6> - while True: - n, prime, elapsed = results.get() # <7> - label = 'P' if prime else ' ' - print(f'{n:16} {label} {elapsed:9.6f}s') # <8> - if jobs.empty(): # <9> - break + workers_done = 0 + checked = 0 + while workers_done < workers: # <7> + n, prime, elapsed = results.get() # <8> + if n == 0: + workers_done += 1 # <9> + else: + checked += 1 + label = 'P' if prime else ' ' + print(f'{n:16} {label} {elapsed:9.6f}s') # <10> elapsed = perf_counter() - t0 - print(f'Total time: {elapsed:.2f}s') + print(f'{checked} checks in {elapsed:.2f}s') # <11> if __name__ == '__main__': main() From 4f4f759229cca84b10170809607849f463891358 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Tue, 21 Sep 2021 23:31:27 -0300 Subject: [PATCH 072/127] sync from Atlas --- 18-with-match/lispy/py3.10/lis.py | 2 +- 19-concurrency/primes/procs.py | 54 +++++++++++++------------ 19-concurrency/primes/threads.py | 66 +++++++++++++++++-------------- 3 files changed, 67 insertions(+), 55 deletions(-) diff --git a/18-with-match/lispy/py3.10/lis.py b/18-with-match/lispy/py3.10/lis.py index 30ad8a8..0faf6d2 100755 --- a/18-with-match/lispy/py3.10/lis.py +++ b/18-with-match/lispy/py3.10/lis.py @@ -69,7 +69,7 @@ def change(self, key: Symbol, value: object) -> None: "Find where key is defined and change the value there." for map in self.maps: if key in map: - map[key] = value + map[key] = value # type: ignore[index] return raise KeyError(key) # end::ENV_CLASS[] diff --git a/19-concurrency/primes/procs.py b/19-concurrency/primes/procs.py index 8624958..09f75e3 100644 --- a/19-concurrency/primes/procs.py +++ b/19-concurrency/primes/procs.py @@ -33,40 +33,44 @@ def worker(jobs: JobQueue, results: ResultQueue) -> None: # <7> results.put(PrimeResult(0, False, 0.0)) # <10> # end::PRIMES_PROC_TOP[] -# tag::PRIMES_PROC_MAIN[] -def main() -> None: - if len(sys.argv) < 2: # <1> - workers = cpu_count() - else: - workers = int(sys.argv[1]) - - print(f'Checking {len(NUMBERS)} numbers with {workers} processes:') - - jobs: JobQueue = SimpleQueue() # <2> - results: ResultQueue = SimpleQueue() - t0 = perf_counter() - - for n in NUMBERS: # <3> - jobs.put(n) - +# tag::PRIMES_PROC_MIDDLE[] +def start_jobs(workers: int, jobs: JobQueue, results: ResultQueue) -> None: + for n in NUMBERS: + jobs.put(n) # <1> for _ in range(workers): - proc = Process(target=worker, args=(jobs, results)) # <4> - proc.start() # <5> - jobs.put(0) # <6> + proc = Process(target=worker, args=(jobs, results)) # <2> + proc.start() # <3> + jobs.put(0) # <4> - workers_done = 0 +def report(workers: int, results: ResultQueue) -> int: checked = 0 - while workers_done < workers: # <7> - n, prime, elapsed = results.get() # <8> + workers_done = 0 + while workers_done < workers: + n, prime, elapsed = results.get() if n == 0: - workers_done += 1 # <9> + workers_done += 1 else: checked += 1 label = 'P' if prime else ' ' - print(f'{n:16} {label} {elapsed:9.6f}s') # <10> + print(f'{n:16} {label} {elapsed:9.6f}s') + return checked +# end::PRIMES_PROC_MIDDLE[] +# tag::PRIMES_PROC_MAIN[] +def main() -> None: + if len(sys.argv) < 2: + workers = cpu_count() + else: + workers = int(sys.argv[1]) + + print(f'Checking {len(NUMBERS)} numbers with {workers} processes:') + t0 = perf_counter() + jobs: JobQueue = SimpleQueue() + results: ResultQueue = SimpleQueue() + start_jobs(workers, jobs, results) + checked = report(workers, results) elapsed = perf_counter() - t0 - print(f'{checked} checks in {elapsed:.2f}s') # <11> + print(f'{checked} checks in {elapsed:.2f}s') if __name__ == '__main__': main() diff --git a/19-concurrency/primes/threads.py b/19-concurrency/primes/threads.py index 0161dd7..ea00bbe 100644 --- a/19-concurrency/primes/threads.py +++ b/19-concurrency/primes/threads.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 """ -threads.py: shows that Python threads are slower than -sequential code for CPU-intensive work. +threads.py: shows that Python threads are slower +than sequential code for CPU-intensive work. """ import os @@ -14,52 +14,60 @@ from primes import is_prime, NUMBERS -class PrimeResult(NamedTuple): # <3> +class PrimeResult(NamedTuple): n: int prime: bool elapsed: float -JobQueue = SimpleQueue[int] -ResultQueue = SimpleQueue[PrimeResult] +JobQueue = SimpleQueue[int] # <4> +ResultQueue = SimpleQueue[PrimeResult] # <5> -def check(n: int) -> PrimeResult: +def check(n: int) -> PrimeResult: # <6> t0 = perf_counter() res = is_prime(n) return PrimeResult(n, res, perf_counter() - t0) -def worker(jobs: JobQueue, results: ResultQueue) -> None: - while n := jobs.get(): - results.put(check(n)) - -def main() -> None: - if len(sys.argv) < 2: # <1> - workers = os.cpu_count() or 1 # make mypy happy - else: - workers = int(sys.argv[1]) - - print(f'Checking {len(NUMBERS)} numbers with {workers} threads:') - - jobs: JobQueue = SimpleQueue() # <2> - results: ResultQueue = SimpleQueue() - t0 = perf_counter() +def worker(jobs: JobQueue, results: ResultQueue) -> None: # <7> + while n := jobs.get(): # <8> + results.put(check(n)) # <9> + results.put(PrimeResult(0, False, 0.0)) +def start_jobs(workers: int, jobs: JobQueue, results: ResultQueue) -> None: for n in NUMBERS: # <3> jobs.put(n) - for _ in range(workers): proc = Thread(target=worker, args=(jobs, results)) # <4> proc.start() # <5> jobs.put(0) # <6> - while True: - n, prime, elapsed = results.get() # <7> - label = 'P' if prime else ' ' - print(f'{n:16} {label} {elapsed:9.6f}s') - if jobs.empty(): # <8> - break +def report(workers: int, results: ResultQueue) -> int: + checked = 0 + workers_done = 0 + while workers_done < workers: + n, prime, elapsed = results.get() + if n == 0: + workers_done += 1 + else: + checked += 1 + label = 'P' if prime else ' ' + print(f'{n:16} {label} {elapsed:9.6f}s') + return checked + +def main() -> None: + if len(sys.argv) < 2: + workers = os.cpu_count() + else: + workers = int(sys.argv[1]) + print(f'Checking {len(NUMBERS)} numbers with {workers} threads:') + t0 = perf_counter() + jobs: JobQueue = SimpleQueue() + results: ResultQueue = SimpleQueue() + start_jobs(workers, jobs, results) + checked = report(workers, results) elapsed = perf_counter() - t0 - print(f'Total time: {elapsed:.2f}s') + print(f'{checked} checks in {elapsed:.2f}s') if __name__ == '__main__': main() + From 4fad21e6b0434d814fab582440a44add48456f04 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 23 Sep 2021 03:07:15 -0300 Subject: [PATCH 073/127] added README.md to 19-concurrency/primes/ --- 19-concurrency/primes/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 19-concurrency/primes/README.md diff --git a/19-concurrency/primes/README.md b/19-concurrency/primes/README.md new file mode 100644 index 0000000..772592a --- /dev/null +++ b/19-concurrency/primes/README.md @@ -0,0 +1,16 @@ +# Race condition in orignal procs.py + +Thanks to reader Michael Albert who noticed the code I published during the Early Release had a race condition in `proc.py`. + +If you are curious, +[this diff](https://github.com/fluentpython/example-code-2e/commit/2c1230579db99738a5e5e6802063bda585f6476d) +shows the bug and how I fixed it—but note that I later refactored +the example to delegate parts of `main` to the `start_jobs` and `report` functions. + +The problem was that I ended the `while` loop that retrieved the results when the `jobs` queue was empty. +However, it was possible that the queue was empty but there were still processes working. +If that happened, one or more results would not be reported. +I did not notice the problem when I tested my original code, +but Albert showed that adding a `sleep(1)` call before the `if jobs.empty()` line made the bug occur frequently. +I adopted one of his solutions: have the `worker` function send back a `PrimeResult` with `n = 0` as a sentinel, +to let the main loop know that the process had completed, ending the loop when all processes were done. \ No newline at end of file From 4002c57728bd5b4874b320ca91284697f708743c Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 27 Sep 2021 15:05:01 -0300 Subject: [PATCH 074/127] added README.adoc --- 20-futures/getflags/README.adoc | 72 +++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 20-futures/getflags/README.adoc diff --git a/20-futures/getflags/README.adoc b/20-futures/getflags/README.adoc new file mode 100644 index 0000000..14ecec4 --- /dev/null +++ b/20-futures/getflags/README.adoc @@ -0,0 +1,72 @@ += Running the flags2* examples + +== Setting up a test server + +If you don't already have a local HTTP server for testing, +here are the steps to experiment using only Python ≥ 3.9—no external libraries: + +. Clone or download the https://github.com/fluentpython/example-code-2e[_Fluent Python 2e_ code repository]. +. Open your shell and go to the _20-futures/getflags/_ directory of your local copy of the repository. +. Unzip the _flags.zip_ file, creating a _flags_ directory at _20-futures/getflags/flags/_. +. Open a second shell, go to the _20-futures/getflags/_ directory and run `python3 -m http.server`. This will start a `ThreadingHTTPServer` listening to port 8000, serving the local files. If you open the URL http://localhost:8000/flags/[http://localhost:8000/flags/] with your browser, you'll see a long list of directories named with two-letter country codes from `ad/` to `zw/`. +. Now you can go back to the first shell and run the _flags2*.py_ examples with the default `--server LOCAL` option. +. To test with the `--server DELAY` option, go to _20-futures/getflags/_ and run `python3 slow_server.py 8001`. This will add a .5s delay before each response. +. To test with the `--server ERROR` option, go to _20-futures/getflags/_ and run `python3 slow_server.py 8002 --error-rate .25`. Each request will have a 25% probability of getting a https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418[418 I'm a teapot] response, and all responses will be delayed .5s. + +I wrote _slow_server.py_ reusing code from Python's +https://github.com/python/cpython/blob/917eca700aa341f8544ace43b75d41b477e98b72/Lib/http/server.py[`http.server`] standard library module, +which "is not recommended for production"—according to the +https://docs.python.org/3/library/http.server.html[documentation]. +To set up a more reliable testing environment, I recommend configuring +https://www.nginx.com/[NGINX] and +https://github.com/shopify/toxiproxy[Toxiproxy] with equivalent parameters. + +== Running a flags2* example + +The `flags2*` examples provide a command-line interface. +All three scripts accept the same options, +and you can see them by running any of the scripts with the `-h` option. +<> shows the help text. + +[[flags2_help_demo]] +.Help screen for the scripts in the flags2 series +==== +[source, text] +---- +$ python3 flags2_threadpool.py -h +usage: flags2_threadpool.py [-h] [-a] [-e] [-l N] [-m CONCURRENT] [-s LABEL] + [-v] + [CC [CC ...]] + +Download flags for country codes. Default: top 20 countries by population. + +positional arguments: + CC country code or 1st letter (eg. B for BA...BZ) + +optional arguments: + -h, --help show this help message and exit + -a, --all get all available flags (AD to ZW) + -e, --every get flags for every possible code (AA...ZZ) + -l N, --limit N limit to N first codes + -m CONCURRENT, --max_req CONCURRENT + maximum concurrent requests (default=30) + -s LABEL, --server LABEL + Server to hit; one of DELAY, ERROR, LOCAL, REMOTE + (default=LOCAL) + -v, --verbose output detailed progress info + +---- +==== + +All arguments are optional. The most important arguments are discussed next. + +One option you can't ignore is `-s/--server`: it lets you choose which HTTP server and base URL will be used in the test. You can pass one of four strings to determine where the script will look for the flags (the strings are case insensitive): + +`LOCAL`:: Use `http://localhost:8000/flags`; this is the default. You should configure a local HTTP server to answer at port 8000. See <> for instructions. + +`REMOTE`:: Use `http://fluentpython.com/data/flags`; that is a public website owned by me, hosted on a shared server. Please do not pound it with too many concurrent requests. The `fluentpython.com` domain is handled by the http://www.cloudflare.com/[Cloudflare] CDN (Content Delivery Network) so you may notice that the first downloads are slower, but they get faster when the CDN cache warms up.footnote:[Before configuring Cloudflare, I got HTTP 503 errors--Service Temporarily Unavailable--when testing the scripts with a few dozen concurrent requests on my inexpensive shared host account. Now those errors are gone.] + +`DELAY`:: Use `http://localhost:8001/flags`; a server delaying HTTP responses should be listening to port 8001. I wrote _slow_server.py_ to make it easier to experiment. You'll find it in the _20-futures/getflags/_ directory of the https://github.com/fluentpython/example-code-2e[_Fluent Python 2e_ code repository]. See <> for instructions. + +`ERROR`:: Use `http://localhost:8002/flags`; a server introducing HTTP errors and delaying responses should be installed at port 8002. Running _slow_server.py_ is an easy way to do it. See <>. + From f7bf5b0d2ad4fb0f5156bc2bb7ca43991fc6719a Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 27 Sep 2021 15:06:49 -0300 Subject: [PATCH 075/127] added README.adoc --- 20-futures/getflags/README.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/20-futures/getflags/README.adoc b/20-futures/getflags/README.adoc index 14ecec4..38cf3df 100644 --- a/20-futures/getflags/README.adoc +++ b/20-futures/getflags/README.adoc @@ -1,4 +1,4 @@ -= Running the flags2* examples += Experimenting with the `flags2*` examples == Setting up a test server @@ -21,7 +21,7 @@ To set up a more reliable testing environment, I recommend configuring https://www.nginx.com/[NGINX] and https://github.com/shopify/toxiproxy[Toxiproxy] with equivalent parameters. -== Running a flags2* example +== Running a `flags2*` script The `flags2*` examples provide a command-line interface. All three scripts accept the same options, From 1e5940056e21b350f2601e5df3f0b24b829d351c Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 27 Sep 2021 19:35:38 -0300 Subject: [PATCH 076/127] ch20: README.adoc --- 20-futures/getflags/README.adoc | 38 +++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/20-futures/getflags/README.adoc b/20-futures/getflags/README.adoc index 38cf3df..ed0b5e9 100644 --- a/20-futures/getflags/README.adoc +++ b/20-futures/getflags/README.adoc @@ -1,25 +1,51 @@ = Experimenting with the `flags2*` examples +== Install SSL Certificates (for MacOS) + +On Macos, depending on how in installed Python you may need to manually run a command +after Python's installer finishes, to install SSL certificates for HTTPS connections. + +Using the Finder, open the `Python 3.X` folder inside `/Applications` folder +and double-click "Install Certificates" or "Install Certificates.command". + +Using the terminal, you can type for example: + +[source, text] +---- +$ open /Applications/Python 3.10/"Install Certificates.command" +---- + + == Setting up a test server If you don't already have a local HTTP server for testing, -here are the steps to experiment using only Python ≥ 3.9—no external libraries: +here are the steps to experiment using only Python ≥ 3.9: -. Clone or download the https://github.com/fluentpython/example-code-2e[_Fluent Python 2e_ code repository]. -. Open your shell and go to the _20-futures/getflags/_ directory of your local copy of the repository. +. Clone or download the https://github.com/fluentpython/example-code-2e[_Fluent Python 2e_ code repository] (this repo!). +. Open your shell and go to the _20-futures/getflags/_ directory of your local copy of the repository (this directory!) . Unzip the _flags.zip_ file, creating a _flags_ directory at _20-futures/getflags/flags/_. . Open a second shell, go to the _20-futures/getflags/_ directory and run `python3 -m http.server`. This will start a `ThreadingHTTPServer` listening to port 8000, serving the local files. If you open the URL http://localhost:8000/flags/[http://localhost:8000/flags/] with your browser, you'll see a long list of directories named with two-letter country codes from `ad/` to `zw/`. . Now you can go back to the first shell and run the _flags2*.py_ examples with the default `--server LOCAL` option. -. To test with the `--server DELAY` option, go to _20-futures/getflags/_ and run `python3 slow_server.py 8001`. This will add a .5s delay before each response. -. To test with the `--server ERROR` option, go to _20-futures/getflags/_ and run `python3 slow_server.py 8002 --error-rate .25`. Each request will have a 25% probability of getting a https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418[418 I'm a teapot] response, and all responses will be delayed .5s. +. To test with the `--server DELAY` option, go to _20-futures/getflags/_ and run `python3 slow_server.py`. This bind to port 8001 by default. It will add a .5s delay before each response. +. To test with the `--server ERROR` option, go to _20-futures/getflags/_ and run `python3 slow_server.py 8002 --error-rate .25`. +Each request will have a 25% probability of getting a +https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418[418 I'm a teapot] response, +and all responses will be delayed .5s. I wrote _slow_server.py_ reusing code from Python's https://github.com/python/cpython/blob/917eca700aa341f8544ace43b75d41b477e98b72/Lib/http/server.py[`http.server`] standard library module, which "is not recommended for production"—according to the https://docs.python.org/3/library/http.server.html[documentation]. -To set up a more reliable testing environment, I recommend configuring + +[NOTE] +==== +This is a simple testing environment that does nor require any external libraries or +tools—apart from the libraries used in the `flags2*` scripts themselves, as discussed in the book. + +For a more robust testing environment, I recommend configuring https://www.nginx.com/[NGINX] and https://github.com/shopify/toxiproxy[Toxiproxy] with equivalent parameters. +==== == Running a `flags2*` script From f45806361f7f7d958c6a61dfb62c04bfb39e8c60 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 27 Sep 2021 19:46:25 -0300 Subject: [PATCH 077/127] ch20: README.adoc --- 20-futures/getflags/README.adoc | 55 +++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/20-futures/getflags/README.adoc b/20-futures/getflags/README.adoc index ed0b5e9..b744c15 100644 --- a/20-futures/getflags/README.adoc +++ b/20-futures/getflags/README.adoc @@ -1,25 +1,25 @@ = Experimenting with the `flags2*` examples -== Install SSL Certificates (for MacOS) +The `flags2*` examples enhance the `flags*` examples with error handling and reporting. +Therefore, we need a server that generates errors and delays to test them. -On Macos, depending on how in installed Python you may need to manually run a command -after Python's installer finishes, to install SSL certificates for HTTPS connections. +The main reason for these instructions is to document how to configure one such server +in your machine, and how to run the clients to use it. -Using the Finder, open the `Python 3.X` folder inside `/Applications` folder -and double-click "Install Certificates" or "Install Certificates.command". +The other reason is to alert of an installation step that MacOS users sometimes overlook. -Using the terminal, you can type for example: - -[source, text] ----- -$ open /Applications/Python 3.10/"Install Certificates.command" ----- +Contents: +* <> +* <> +* <> +[[server_setup]] == Setting up a test server If you don't already have a local HTTP server for testing, -here are the steps to experiment using only Python ≥ 3.9: +here are the steps to experiment with the `flags2*` examples +using just the Python ≥ 3.9 distribution and . Clone or download the https://github.com/fluentpython/example-code-2e[_Fluent Python 2e_ code repository] (this repo!). . Open your shell and go to the _20-futures/getflags/_ directory of your local copy of the repository (this directory!) @@ -39,7 +39,7 @@ https://docs.python.org/3/library/http.server.html[documentation]. [NOTE] ==== -This is a simple testing environment that does nor require any external libraries or +This is a simple testing environment that does not require any external libraries or tools—apart from the libraries used in the `flags2*` scripts themselves, as discussed in the book. For a more robust testing environment, I recommend configuring @@ -47,6 +47,7 @@ https://www.nginx.com/[NGINX] and https://github.com/shopify/toxiproxy[Toxiproxy] with equivalent parameters. ==== +[[client_setup]] == Running a `flags2*` script The `flags2*` examples provide a command-line interface. @@ -86,13 +87,35 @@ optional arguments: All arguments are optional. The most important arguments are discussed next. -One option you can't ignore is `-s/--server`: it lets you choose which HTTP server and base URL will be used in the test. You can pass one of four strings to determine where the script will look for the flags (the strings are case insensitive): +One option you can't ignore is `-s/--server`: it lets you choose which HTTP server and base URL will be used in the test. +You can pass one of four strings to determine where the script will look for the flags (the strings are case insensitive): -`LOCAL`:: Use `http://localhost:8000/flags`; this is the default. You should configure a local HTTP server to answer at port 8000. See <> for instructions. +`LOCAL`:: Use `http://localhost:8000/flags`; this is the default. +You should configure a local HTTP server to answer at port 8000. See <> for instructions. -`REMOTE`:: Use `http://fluentpython.com/data/flags`; that is a public website owned by me, hosted on a shared server. Please do not pound it with too many concurrent requests. The `fluentpython.com` domain is handled by the http://www.cloudflare.com/[Cloudflare] CDN (Content Delivery Network) so you may notice that the first downloads are slower, but they get faster when the CDN cache warms up.footnote:[Before configuring Cloudflare, I got HTTP 503 errors--Service Temporarily Unavailable--when testing the scripts with a few dozen concurrent requests on my inexpensive shared host account. Now those errors are gone.] +`REMOTE`:: Use `http://fluentpython.com/data/flags`; that is a public website owned by me, hosted on a shared server. +Please do not pound it with too many concurrent requests. +The `fluentpython.com` domain is handled by the http://www.cloudflare.com/[Cloudflare] CDN (Content Delivery Network) +so you may notice that the first downloads are slower, but they get faster when the CDN cache warms +up.footnote:[Before configuring Cloudflare, I got HTTP 503 errors--Service Temporarily Unavailable--when +testing the scripts with a few dozen concurrent requests on my inexpensive shared host account. Now those errors are gone.] `DELAY`:: Use `http://localhost:8001/flags`; a server delaying HTTP responses should be listening to port 8001. I wrote _slow_server.py_ to make it easier to experiment. You'll find it in the _20-futures/getflags/_ directory of the https://github.com/fluentpython/example-code-2e[_Fluent Python 2e_ code repository]. See <> for instructions. `ERROR`:: Use `http://localhost:8002/flags`; a server introducing HTTP errors and delaying responses should be installed at port 8002. Running _slow_server.py_ is an easy way to do it. See <>. +[[macos_certificates]] +== Install SSL Certificates (for MacOS) + +On Macos, depending on how in installed Python you may need to manually run a command +after Python's installer finishes, to install SSL certificates for HTTPS connections. + +Using the Finder, open the `Python 3.X` folder inside `/Applications` folder +and double-click "Install Certificates" or "Install Certificates.command". + +Using the terminal, you can type for example: + +[source, text] +---- +$ open /Applications/Python 3.10/"Install Certificates.command" +---- From 69f326dd3dfef93565a185ee9daa9cbf9a69d09a Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 27 Sep 2021 19:55:48 -0300 Subject: [PATCH 078/127] ch20: README.adoc --- 20-futures/getflags/README.adoc | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/20-futures/getflags/README.adoc b/20-futures/getflags/README.adoc index b744c15..cfbb255 100644 --- a/20-futures/getflags/README.adoc +++ b/20-futures/getflags/README.adoc @@ -1,11 +1,10 @@ = Experimenting with the `flags2*` examples The `flags2*` examples enhance the `flags*` examples with error handling and reporting. -Therefore, we need a server that generates errors and delays to test them. +Therefore, we need a server that generates errors and delays to experiment with them. The main reason for these instructions is to document how to configure one such server -in your machine, and how to run the clients to use it. - +in your machine, and how to tell the `flags2*` clients to access it. The other reason is to alert of an installation step that MacOS users sometimes overlook. Contents: @@ -19,14 +18,14 @@ Contents: If you don't already have a local HTTP server for testing, here are the steps to experiment with the `flags2*` examples -using just the Python ≥ 3.9 distribution and +using just the Python ≥ 3.9 distribution: . Clone or download the https://github.com/fluentpython/example-code-2e[_Fluent Python 2e_ code repository] (this repo!). . Open your shell and go to the _20-futures/getflags/_ directory of your local copy of the repository (this directory!) . Unzip the _flags.zip_ file, creating a _flags_ directory at _20-futures/getflags/flags/_. . Open a second shell, go to the _20-futures/getflags/_ directory and run `python3 -m http.server`. This will start a `ThreadingHTTPServer` listening to port 8000, serving the local files. If you open the URL http://localhost:8000/flags/[http://localhost:8000/flags/] with your browser, you'll see a long list of directories named with two-letter country codes from `ad/` to `zw/`. . Now you can go back to the first shell and run the _flags2*.py_ examples with the default `--server LOCAL` option. -. To test with the `--server DELAY` option, go to _20-futures/getflags/_ and run `python3 slow_server.py`. This bind to port 8001 by default. It will add a .5s delay before each response. +. To test with the `--server DELAY` option, go to _20-futures/getflags/_ and run `python3 slow_server.py`. This binds to port 8001 by default. It will add a .5s delay before each response. . To test with the `--server ERROR` option, go to _20-futures/getflags/_ and run `python3 slow_server.py 8002 --error-rate .25`. Each request will have a 25% probability of getting a https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418[418 I'm a teapot] response, @@ -52,8 +51,7 @@ https://github.com/shopify/toxiproxy[Toxiproxy] with equivalent parameters. The `flags2*` examples provide a command-line interface. All three scripts accept the same options, -and you can see them by running any of the scripts with the `-h` option. -<> shows the help text. +and you can see them by running any of the scripts with the `-h` option: [[flags2_help_demo]] .Help screen for the scripts in the flags2 series @@ -91,18 +89,21 @@ One option you can't ignore is `-s/--server`: it lets you choose which HTTP serv You can pass one of four strings to determine where the script will look for the flags (the strings are case insensitive): `LOCAL`:: Use `http://localhost:8000/flags`; this is the default. -You should configure a local HTTP server to answer at port 8000. See <> for instructions. +You should configure a local HTTP server to answer at port 8000. See <> for instructions. +Feel free to hit this as hard as you can. It's your machine! `REMOTE`:: Use `http://fluentpython.com/data/flags`; that is a public website owned by me, hosted on a shared server. -Please do not pound it with too many concurrent requests. +Please do not hit it with too many concurrent requests. The `fluentpython.com` domain is handled by the http://www.cloudflare.com/[Cloudflare] CDN (Content Delivery Network) so you may notice that the first downloads are slower, but they get faster when the CDN cache warms up.footnote:[Before configuring Cloudflare, I got HTTP 503 errors--Service Temporarily Unavailable--when testing the scripts with a few dozen concurrent requests on my inexpensive shared host account. Now those errors are gone.] -`DELAY`:: Use `http://localhost:8001/flags`; a server delaying HTTP responses should be listening to port 8001. I wrote _slow_server.py_ to make it easier to experiment. You'll find it in the _20-futures/getflags/_ directory of the https://github.com/fluentpython/example-code-2e[_Fluent Python 2e_ code repository]. See <> for instructions. +`DELAY`:: Use `http://localhost:8001/flags`; a server delaying HTTP responses should be listening to port 8001. +I wrote _slow_server.py_ to make it easier to experiment. See <> for instructions. -`ERROR`:: Use `http://localhost:8002/flags`; a server introducing HTTP errors and delaying responses should be installed at port 8002. Running _slow_server.py_ is an easy way to do it. See <>. +`ERROR`:: Use `http://localhost:8002/flags`; a server introducing HTTP errors and delaying responses should be installed at port 8002. +Running _slow_server.py_ is an easy way to do it. See <>. [[macos_certificates]] == Install SSL Certificates (for MacOS) From 7985fda09f4c2f644540a258d141be92161c5eaf Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 27 Sep 2021 19:58:16 -0300 Subject: [PATCH 079/127] ch20: README.adoc --- 20-futures/getflags/README.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/20-futures/getflags/README.adoc b/20-futures/getflags/README.adoc index cfbb255..b4b3cc2 100644 --- a/20-futures/getflags/README.adoc +++ b/20-futures/getflags/README.adoc @@ -108,8 +108,8 @@ Running _slow_server.py_ is an easy way to do it. See <>. [[macos_certificates]] == Install SSL Certificates (for MacOS) -On Macos, depending on how in installed Python you may need to manually run a command -after Python's installer finishes, to install SSL certificates for HTTPS connections. +On Macos, depending on how you installed Python you may need to manually run a command +after Python's installer finishes, to install the SSL certificates Python uses to make HTTPS connections. Using the Finder, open the `Python 3.X` folder inside `/Applications` folder and double-click "Install Certificates" or "Install Certificates.command". From 4f1392d21c87e2519bd212fef8d9cbe280f7533a Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 2 Oct 2021 17:12:42 -0300 Subject: [PATCH 080/127] sequential, threaded & async HTTP clients using HTTPX --- 20-futures/getflags/README.adoc | 6 +- 20-futures/getflags/flags.py | 21 ++-- 20-futures/getflags/flags2_asyncio.py | 89 +++++++------- .../getflags/flags2_asyncio_executor.py | 115 +++++++++--------- 20-futures/getflags/flags2_common.py | 30 ++--- 20-futures/getflags/flags2_sequential.py | 59 ++++----- 20-futures/getflags/flags2_threadpool.py | 51 ++++---- 20-futures/getflags/flags3_asyncio.py | 25 ++-- 20-futures/getflags/flags_asyncio.py | 26 ++-- 20-futures/getflags/requirements.txt | 19 ++- 20-futures/getflags/slow_server.py | 20 ++- 20-futures/getflags/tree.py | 38 ++++++ 12 files changed, 273 insertions(+), 226 deletions(-) create mode 100644 20-futures/getflags/tree.py diff --git a/20-futures/getflags/README.adoc b/20-futures/getflags/README.adoc index b4b3cc2..a649994 100644 --- a/20-futures/getflags/README.adoc +++ b/20-futures/getflags/README.adoc @@ -14,7 +14,7 @@ Contents: * <> [[server_setup]] -== Setting up a test server +== Setting up test servers If you don't already have a local HTTP server for testing, here are the steps to experiment with the `flags2*` examples @@ -25,7 +25,7 @@ using just the Python ≥ 3.9 distribution: . Unzip the _flags.zip_ file, creating a _flags_ directory at _20-futures/getflags/flags/_. . Open a second shell, go to the _20-futures/getflags/_ directory and run `python3 -m http.server`. This will start a `ThreadingHTTPServer` listening to port 8000, serving the local files. If you open the URL http://localhost:8000/flags/[http://localhost:8000/flags/] with your browser, you'll see a long list of directories named with two-letter country codes from `ad/` to `zw/`. . Now you can go back to the first shell and run the _flags2*.py_ examples with the default `--server LOCAL` option. -. To test with the `--server DELAY` option, go to _20-futures/getflags/_ and run `python3 slow_server.py`. This binds to port 8001 by default. It will add a .5s delay before each response. +. To test with the `--server DELAY` option, go to _20-futures/getflags/_ and run `python3 slow_server.py`. This binds to port 8001 by default. It will add a random delay of .5s to 5s before each response. . To test with the `--server ERROR` option, go to _20-futures/getflags/_ and run `python3 slow_server.py 8002 --error-rate .25`. Each request will have a 25% probability of getting a https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418[418 I'm a teapot] response, @@ -86,7 +86,7 @@ optional arguments: All arguments are optional. The most important arguments are discussed next. One option you can't ignore is `-s/--server`: it lets you choose which HTTP server and base URL will be used in the test. -You can pass one of four strings to determine where the script will look for the flags (the strings are case insensitive): +You can pass one of four labels to determine where the script will look for the flags (the labels are case-insensitive): `LOCAL`:: Use `http://localhost:8000/flags`; this is the default. You should configure a local HTTP server to answer at port 8000. See <> for instructions. diff --git a/20-futures/getflags/flags.py b/20-futures/getflags/flags.py index 3d6d36f..2311125 100755 --- a/20-futures/getflags/flags.py +++ b/20-futures/getflags/flags.py @@ -21,12 +21,12 @@ from pathlib import Path from typing import Callable -import requests # <1> +import httpx # <1> POP20_CC = ('CN IN US ID BR PK NG BD RU JP ' 'MX PH VN ET EG DE IR TR CD FR').split() # <2> -BASE_URL = 'http://fluentpython.com/data/flags' # <3> +BASE_URL = 'https://www.fluentpython.com/data/flags' # <3> DEST_DIR = Path('downloaded') # <4> def save_flag(img: bytes, filename: str) -> None: # <5> @@ -34,22 +34,25 @@ def save_flag(img: bytes, filename: str) -> None: # <5> def get_flag(cc: str) -> bytes: # <6> url = f'{BASE_URL}/{cc}/{cc}.gif'.lower() - resp = requests.get(url) + resp = httpx.get(url, timeout=6.1, # <7> + follow_redirects=True) # <8> + resp.raise_for_status() # <9> return resp.content -def download_many(cc_list: list[str]) -> int: # <7> - for cc in sorted(cc_list): # <8> +def download_many(cc_list: list[str]) -> int: # <10> + for cc in sorted(cc_list): # <11> image = get_flag(cc) save_flag(image, f'{cc}.gif') - print(cc, end=' ', flush=True) # <9> + print(cc, end=' ', flush=True) # <12> return len(cc_list) -def main(downloader: Callable[[list[str]], int]) -> None: # <10> - t0 = time.perf_counter() # <11> +def main(downloader: Callable[[list[str]], int]) -> None: # <13> + DEST_DIR.mkdir(exist_ok=True) # <14> + t0 = time.perf_counter() # <15> count = downloader(POP20_CC) elapsed = time.perf_counter() - t0 print(f'\n{count} downloads in {elapsed:.2f}s') if __name__ == '__main__': - main(download_many) # <12> + main(download_many) # <16> # end::FLAGS_PY[] diff --git a/20-futures/getflags/flags2_asyncio.py b/20-futures/getflags/flags2_asyncio.py index 88a18ed..a2bb6ff 100755 --- a/20-futures/getflags/flags2_asyncio.py +++ b/20-futures/getflags/flags2_asyncio.py @@ -8,65 +8,60 @@ # tag::FLAGS2_ASYNCIO_TOP[] import asyncio from collections import Counter +from http import HTTPStatus +from pathlib import Path -import aiohttp +import httpx import tqdm # type: ignore -from flags2_common import main, HTTPStatus, Result, save_flag +from flags2_common import main, DownloadStatus, save_flag # default set low to avoid errors from remote site, such as # 503 - Service Temporarily Unavailable DEFAULT_CONCUR_REQ = 5 MAX_CONCUR_REQ = 1000 - -class FetchError(Exception): # <1> - def __init__(self, country_code: str): - self.country_code = country_code - - -async def get_flag(session: aiohttp.ClientSession, # <2> +async def get_flag(session: httpx.AsyncClient, # <2> base_url: str, cc: str) -> bytes: url = f'{base_url}/{cc}/{cc}.gif'.lower() - async with session.get(url) as resp: - if resp.status == 200: - return await resp.read() - else: - resp.raise_for_status() # <3> - return bytes() + resp = await session.get(url, timeout=3.1, follow_redirects=True) # <3> + resp.raise_for_status() + return resp.content -async def download_one(session: aiohttp.ClientSession, +async def download_one(session: httpx.AsyncClient, cc: str, base_url: str, semaphore: asyncio.Semaphore, # <4> - verbose: bool) -> Result: + verbose: bool) -> DownloadStatus: try: async with semaphore: # <5> image = await get_flag(session, base_url, cc) - except aiohttp.ClientResponseError as exc: - if exc.status == 404: # <6> - status = HTTPStatus.not_found - msg = 'not found' + except httpx.HTTPStatusError as exc: # <4> + res = exc.response + if res.status_code == HTTPStatus.NOT_FOUND: + status = DownloadStatus.NOT_FOUND # <5> + msg = f'not found: {res.url}' else: - raise FetchError(cc) from exc # <7> + raise + else: - save_flag(image, f'{cc}.gif') - status = HTTPStatus.ok + await asyncio.to_thread(save_flag, image, f'{cc}.gif') + status = DownloadStatus.OK msg = 'OK' if verbose and msg: print(cc, msg) - return Result(status, cc) + return status # end::FLAGS2_ASYNCIO_TOP[] # tag::FLAGS2_ASYNCIO_START[] async def supervisor(cc_list: list[str], base_url: str, verbose: bool, - concur_req: int) -> Counter[HTTPStatus]: # <1> - counter: Counter[HTTPStatus] = Counter() + concur_req: int) -> Counter[DownloadStatus]: # <1> + counter: Counter[DownloadStatus] = Counter() semaphore = asyncio.Semaphore(concur_req) # <2> - async with aiohttp.ClientSession() as session: + async with httpx.AsyncClient() as session: to_do = [download_one(session, cc, base_url, semaphore, verbose) for cc in sorted(cc_list)] # <3> to_do_iter = asyncio.as_completed(to_do) # <4> @@ -74,25 +69,33 @@ async def supervisor(cc_list: list[str], to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list)) # <5> for coro in to_do_iter: # <6> try: - res = await coro # <7> - except FetchError as exc: # <8> - country_code = exc.country_code # <9> - try: - error_msg = exc.__cause__.message # type: ignore # <10> - except AttributeError: - error_msg = 'Unknown cause' # <11> - if verbose and error_msg: - print(f'*** Error for {country_code}: {error_msg}') - status = HTTPStatus.error - else: - status = res.status - counter[status] += 1 # <12> - return counter # <13> + status = await coro # <7> + except httpx.HTTPStatusError as exc: # <8> + error_msg = 'HTTP error {resp.status_code} - {resp.reason_phrase}' + error_msg = error_msg.format(resp=exc.response) + error = exc + except httpx.RequestError as exc: # <9> + error_msg = f'{exc} {type(exc)}'.strip() + error = exc + except KeyboardInterrupt: # <10> + break + else: # <11> + error = None + + if error: + status = DownloadStatus.ERROR # <12> + if verbose: + url = str(error.request.url) # <13> + cc = Path(url).stem.upper() # <14> + print(f'{cc} error: {error_msg}') + counter[status] += 1 + + return counter def download_many(cc_list: list[str], base_url: str, verbose: bool, - concur_req: int) -> Counter[HTTPStatus]: + concur_req: int) -> Counter[DownloadStatus]: coro = supervisor(cc_list, base_url, verbose, concur_req) counts = asyncio.run(coro) # <14> diff --git a/20-futures/getflags/flags2_asyncio_executor.py b/20-futures/getflags/flags2_asyncio_executor.py index 5b75d59..7143b46 100755 --- a/20-futures/getflags/flags2_asyncio_executor.py +++ b/20-futures/getflags/flags2_asyncio_executor.py @@ -2,17 +2,19 @@ """Download flags of countries (with error handling). -asyncio async/await version using run_in_executor for save_flag. +asyncio async/await version """ - +# tag::FLAGS2_ASYNCIO_TOP[] import asyncio from collections import Counter +from http import HTTPStatus +from pathlib import Path -import aiohttp +import httpx import tqdm # type: ignore -from flags2_common import main, HTTPStatus, Result, save_flag +from flags2_common import main, DownloadStatus, save_flag # default set low to avoid errors from remote site, such as # 503 - Service Temporarily Unavailable @@ -20,90 +22,87 @@ MAX_CONCUR_REQ = 1000 -class FetchError(Exception): - def __init__(self, country_code: str): - self.country_code = country_code - - -async def get_flag(session: aiohttp.ClientSession, +async def get_flag(session: httpx.AsyncClient, # <2> base_url: str, cc: str) -> bytes: url = f'{base_url}/{cc}/{cc}.gif'.lower() - async with session.get(url) as resp: - if resp.status == 200: - return await resp.read() - else: - resp.raise_for_status() - return bytes() + resp = await session.get(url, timeout=3.1, follow_redirects=True) # <3> + resp.raise_for_status() + return resp.content -# tag::FLAGS2_ASYNCIO_EXECUTOR[] -async def download_one(session: aiohttp.ClientSession, + +async def download_one(session: httpx.AsyncClient, cc: str, base_url: str, semaphore: asyncio.Semaphore, - verbose: bool) -> Result: + verbose: bool) -> DownloadStatus: try: async with semaphore: image = await get_flag(session, base_url, cc) - except aiohttp.ClientResponseError as exc: - if exc.status == 404: - status = HTTPStatus.not_found - msg = 'not found' + except httpx.HTTPStatusError as exc: + res = exc.response + if res.status_code == HTTPStatus.NOT_FOUND: + status = DownloadStatus.NOT_FOUND + msg = f'not found: {res.url}' else: - raise FetchError(cc) from exc + raise else: - loop = asyncio.get_running_loop() # <1> - loop.run_in_executor(None, # <2> - save_flag, image, f'{cc}.gif') # <3> - status = HTTPStatus.ok +# tag::FLAGS2_ASYNCIO_EXECUTOR[] + loop = asyncio.get_running_loop() # <1> + loop.run_in_executor(None, save_flag, # <2> + image, f'{cc}.gif') # <3> +# end::FLAGS2_ASYNCIO_EXECUTOR[] + status = DownloadStatus.OK msg = 'OK' if verbose and msg: print(cc, msg) - return Result(status, cc) -# end::FLAGS2_ASYNCIO_EXECUTOR[] + return status async def supervisor(cc_list: list[str], base_url: str, verbose: bool, - concur_req: int) -> Counter[HTTPStatus]: - counter: Counter[HTTPStatus] = Counter() - semaphore = asyncio.Semaphore(concur_req) - async with aiohttp.ClientSession() as session: + concur_req: int) -> Counter[DownloadStatus]: # <1> + counter: Counter[DownloadStatus] = Counter() + semaphore = asyncio.Semaphore(concur_req) # <2> + async with httpx.AsyncClient() as session: to_do = [download_one(session, cc, base_url, semaphore, verbose) - for cc in sorted(cc_list)] - - to_do_iter = asyncio.as_completed(to_do) + for cc in sorted(cc_list)] # <3> + to_do_iter = asyncio.as_completed(to_do) # <4> if not verbose: - to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list)) - for coro in to_do_iter: + to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list)) # <5> + for coro in to_do_iter: # <6> try: - res = await coro - except FetchError as exc: - country_code = exc.country_code - try: - error_msg = exc.__cause__.message # type: ignore - except AttributeError: - error_msg = 'Unknown cause' - if verbose and error_msg: - print(f'*** Error for {country_code}: {error_msg}') - status = HTTPStatus.error - else: - status = res.status - - counter[status] += 1 - - return counter - + status = await coro # <7> + except httpx.HTTPStatusError as exc: # <13> + error_msg = 'HTTP error {resp.status_code} - {resp.reason_phrase}' + error_msg = error_msg.format(resp=exc.response) + error = exc + except httpx.RequestError as exc: # <15> + error_msg = f'{exc} {type(exc)}'.strip() + error = exc + except KeyboardInterrupt: # <7> + break + else: # <8> + error = None + + if error: + status = DownloadStatus.ERROR # <9> + if verbose: # <11> + cc = Path(str(error.request.url)).stem.upper() + print(f'{cc} error: {error_msg}') + counter[status] += 1 # <10> + + return counter # <12> def download_many(cc_list: list[str], base_url: str, verbose: bool, - concur_req: int) -> Counter[HTTPStatus]: + concur_req: int) -> Counter[DownloadStatus]: coro = supervisor(cc_list, base_url, verbose, concur_req) counts = asyncio.run(coro) # <14> return counts - if __name__ == '__main__': main(download_many, DEFAULT_CONCUR_REQ, MAX_CONCUR_REQ) +# end::FLAGS2_ASYNCIO_START[] diff --git a/20-futures/getflags/flags2_common.py b/20-futures/getflags/flags2_common.py index 74cd888..1ad05d3 100644 --- a/20-futures/getflags/flags2_common.py +++ b/20-futures/getflags/flags2_common.py @@ -5,13 +5,11 @@ import string import sys import time -from collections import namedtuple, Counter +from collections import Counter from enum import Enum from pathlib import Path -Result = namedtuple('Result', 'status data') - -HTTPStatus = Enum('HTTPStatus', 'ok not_found error') +DownloadStatus = Enum('DownloadStatus', 'OK NOT_FOUND ERROR') POP20_CC = ('CN IN US ID BR PK NG BD RU JP ' 'MX PH VN ET EG DE IR TR CD FR').split() @@ -20,7 +18,7 @@ MAX_CONCUR_REQ = 1 SERVERS = { - 'REMOTE': 'http://fluentpython.com/data/flags', + 'REMOTE': 'https://www.fluentpython.com/data/flags', 'LOCAL': 'http://localhost:8000/flags', 'DELAY': 'http://localhost:8001/flags', 'ERROR': 'http://localhost:8002/flags', @@ -52,17 +50,17 @@ def initial_report(cc_list: list[str], def final_report(cc_list: list[str], - counter: Counter[HTTPStatus], + counter: Counter[DownloadStatus], start_time: float) -> None: elapsed = time.perf_counter() - start_time print('-' * 20) - plural = 's' if counter[HTTPStatus.ok] != 1 else '' - print(f'{counter[HTTPStatus.ok]} flag{plural} downloaded.') - if counter[HTTPStatus.not_found]: - print(f'{counter[HTTPStatus.not_found]} not found.') - if counter[HTTPStatus.error]: - plural = 's' if counter[HTTPStatus.error] != 1 else '' - print(f'{counter[HTTPStatus.error]} error{plural}.') + plural = 's' if counter[DownloadStatus.OK] != 1 else '' + print(f'{counter[DownloadStatus.OK]:3} flag{plural} downloaded.') + if counter[DownloadStatus.NOT_FOUND]: + print(f'{counter[DownloadStatus.NOT_FOUND]:3} not found.') + if counter[DownloadStatus.ERROR]: + plural = 's' if counter[DownloadStatus.ERROR] != 1 else '' + print(f'{counter[DownloadStatus.ERROR]:3} error{plural}.') print(f'Elapsed time: {elapsed:.2f}s') @@ -142,7 +140,7 @@ def process_args(default_concur_req): sys.exit(2) # command line usage error if not cc_list: - cc_list = sorted(POP20_CC) + cc_list = sorted(POP20_CC)[:args.limit] return args, cc_list @@ -151,9 +149,7 @@ def main(download_many, default_concur_req, max_concur_req): actual_req = min(args.max_req, max_concur_req, len(cc_list)) initial_report(cc_list, actual_req, args.server) base_url = SERVERS[args.server] + DEST_DIR.mkdir(exist_ok=True) t0 = time.perf_counter() counter = download_many(cc_list, base_url, args.verbose, actual_req) - assert sum(counter.values()) == len(cc_list), ( - 'some downloads are unaccounted for' - ) final_report(cc_list, counter, t0) diff --git a/20-futures/getflags/flags2_sequential.py b/20-futures/getflags/flags2_sequential.py index 2e30856..e4358a3 100755 --- a/20-futures/getflags/flags2_sequential.py +++ b/20-futures/getflags/flags2_sequential.py @@ -17,71 +17,72 @@ """ +# tag::FLAGS2_BASIC_HTTP_FUNCTIONS[] from collections import Counter +from http import HTTPStatus -import requests -import tqdm # type: ignore +import httpx +import tqdm # type: ignore # <1> -from flags2_common import main, save_flag, HTTPStatus, Result +from flags2_common import main, save_flag, DownloadStatus # <2> DEFAULT_CONCUR_REQ = 1 MAX_CONCUR_REQ = 1 -# tag::FLAGS2_BASIC_HTTP_FUNCTIONS[] def get_flag(base_url: str, cc: str) -> bytes: url = f'{base_url}/{cc}/{cc}.gif'.lower() - resp = requests.get(url) - if resp.status_code != 200: # <1> - resp.raise_for_status() + resp = httpx.get(url, timeout=3.1, follow_redirects=True) + resp.raise_for_status() # <3> return resp.content -def download_one(cc: str, base_url: str, verbose: bool = False): +def download_one(cc: str, base_url: str, verbose: bool = False) -> DownloadStatus: try: image = get_flag(base_url, cc) - except requests.exceptions.HTTPError as exc: # <2> + except httpx.HTTPStatusError as exc: # <4> res = exc.response - if res.status_code == 404: - status = HTTPStatus.not_found # <3> - msg = 'not found' - else: # <4> - raise + if res.status_code == HTTPStatus.NOT_FOUND: + status = DownloadStatus.NOT_FOUND # <5> + msg = f'not found: {res.url}' + else: + raise # <6> else: save_flag(image, f'{cc}.gif') - status = HTTPStatus.ok + status = DownloadStatus.OK msg = 'OK' - if verbose: # <5> + if verbose: # <7> print(cc, msg) - return Result(status, cc) # <6> + return status # end::FLAGS2_BASIC_HTTP_FUNCTIONS[] # tag::FLAGS2_DOWNLOAD_MANY_SEQUENTIAL[] def download_many(cc_list: list[str], base_url: str, verbose: bool, - _unused_concur_req: int) -> Counter[int]: - counter: Counter[int] = Counter() # <1> + _unused_concur_req: int) -> Counter[DownloadStatus]: + counter: Counter[DownloadStatus] = Counter() # <1> cc_iter = sorted(cc_list) # <2> if not verbose: cc_iter = tqdm.tqdm(cc_iter) # <3> - for cc in cc_iter: # <4> + for cc in cc_iter: try: - res = download_one(cc, base_url, verbose) # <5> - except requests.exceptions.HTTPError as exc: # <6> - error_msg = 'HTTP error {res.status_code} - {res.reason}' - error_msg = error_msg.format(res=exc.response) - except requests.exceptions.ConnectionError: # <7> - error_msg = 'Connection error' + status = download_one(cc, base_url, verbose) # <4> + except httpx.HTTPStatusError as exc: # <5> + error_msg = 'HTTP error {resp.status_code} - {resp.reason_phrase}' + error_msg = error_msg.format(resp=exc.response) + except httpx.RequestError as exc: # <6> + error_msg = f'{exc} {type(exc)}'.strip() + except KeyboardInterrupt: # <7> + break else: # <8> error_msg = '' - status = res.status if error_msg: - status = HTTPStatus.error # <9> + status = DownloadStatus.ERROR # <9> counter[status] += 1 # <10> if verbose and error_msg: # <11> - print(f'*** Error for {cc}: {error_msg}') + print(f'{cc} error: {error_msg}') return counter # <12> # end::FLAGS2_DOWNLOAD_MANY_SEQUENTIAL[] diff --git a/20-futures/getflags/flags2_threadpool.py b/20-futures/getflags/flags2_threadpool.py index bbe71cb..8955c7c 100755 --- a/20-futures/getflags/flags2_threadpool.py +++ b/20-futures/getflags/flags2_threadpool.py @@ -22,48 +22,49 @@ from collections import Counter from concurrent import futures -import requests -import tqdm # type: ignore # <1> +import httpx +import tqdm # type: ignore -from flags2_common import main, HTTPStatus # <2> -from flags2_sequential import download_one # <3> +from flags2_common import main, DownloadStatus +from flags2_sequential import download_one # <1> -DEFAULT_CONCUR_REQ = 30 # <4> -MAX_CONCUR_REQ = 1000 # <5> +DEFAULT_CONCUR_REQ = 30 # <2> +MAX_CONCUR_REQ = 1000 # <3> def download_many(cc_list: list[str], base_url: str, verbose: bool, - concur_req: int) -> Counter[int]: - counter: Counter[int] = Counter() - with futures.ThreadPoolExecutor(max_workers=concur_req) as executor: # <6> - to_do_map = {} # <7> - for cc in sorted(cc_list): # <8> + concur_req: int) -> Counter[DownloadStatus]: + counter: Counter[DownloadStatus] = Counter() + with futures.ThreadPoolExecutor(max_workers=concur_req) as executor: # <4> + to_do_map = {} # <5> + for cc in sorted(cc_list): # <6> future = executor.submit(download_one, cc, - base_url, verbose) # <9> - to_do_map[future] = cc # <10> - done_iter = futures.as_completed(to_do_map) # <11> + base_url, verbose) # <7> + to_do_map[future] = cc # <8> + done_iter = futures.as_completed(to_do_map) # <9> if not verbose: - done_iter = tqdm.tqdm(done_iter, total=len(cc_list)) # <12> - for future in done_iter: # <13> + done_iter = tqdm.tqdm(done_iter, total=len(cc_list)) # <10> + for future in done_iter: # <11> try: - res = future.result() # <14> - except requests.exceptions.HTTPError as exc: # <15> - error_fmt = 'HTTP {res.status_code} - {res.reason}' - error_msg = error_fmt.format(res=exc.response) - except requests.exceptions.ConnectionError: - error_msg = 'Connection error' + status = future.result() # <12> + except httpx.HTTPStatusError as exc: # <13> + error_msg = 'HTTP error {resp.status_code} - {resp.reason_phrase}' + error_msg = error_msg.format(resp=exc.response) + except httpx.RequestError as exc: # <15> + error_msg = f'{exc} {type(exc)}'.strip() + except KeyboardInterrupt: + break else: error_msg = '' - status = res.status if error_msg: - status = HTTPStatus.error + status = DownloadStatus.ERROR counter[status] += 1 if verbose and error_msg: cc = to_do_map[future] # <16> - print(f'*** Error for {cc}: {error_msg}') + print(f'{cc} error: {error_msg}') return counter diff --git a/20-futures/getflags/flags3_asyncio.py b/20-futures/getflags/flags3_asyncio.py index 04a01e2..b698d4d 100755 --- a/20-futures/getflags/flags3_asyncio.py +++ b/20-futures/getflags/flags3_asyncio.py @@ -8,11 +8,12 @@ import asyncio from collections import Counter +from http import HTTPStatus import aiohttp import tqdm # type: ignore -from flags2_common import main, HTTPStatus, Result, save_flag +from flags2_common import main, DownloadStatus, save_flag # default set low to avoid errors from remote site, such as # 503 - Service Temporarily Unavailable @@ -54,15 +55,15 @@ async def download_one(session: aiohttp.ClientSession, cc: str, base_url: str, semaphore: asyncio.Semaphore, - verbose: bool) -> Result: + verbose: bool) -> DownloadStatus: try: async with semaphore: image = await get_flag(session, base_url, cc) # <1> async with semaphore: country = await get_country(session, base_url, cc) # <2> except aiohttp.ClientResponseError as exc: - if exc.status == 404: - status = HTTPStatus.not_found + if exc.status == HTTPStatus.NOT_FOUND: + status = DownloadStatus.NOT_FOUND msg = 'not found' else: raise FetchError(cc) from exc @@ -72,18 +73,18 @@ async def download_one(session: aiohttp.ClientSession, loop = asyncio.get_running_loop() loop.run_in_executor(None, save_flag, image, filename) - status = HTTPStatus.ok + status = DownloadStatus.OK msg = 'OK' if verbose and msg: print(cc, msg) - return Result(status, cc) + return status # end::FLAGS3_ASYNCIO_DOWNLOAD_ONE[] async def supervisor(cc_list: list[str], base_url: str, verbose: bool, - concur_req: int) -> Counter[HTTPStatus]: - counter: Counter[HTTPStatus] = Counter() + concur_req: int) -> Counter[DownloadStatus]: + counter: Counter[DownloadStatus] = Counter() semaphore = asyncio.Semaphore(concur_req) async with aiohttp.ClientSession() as session: to_do = [download_one(session, cc, base_url, @@ -95,7 +96,7 @@ async def supervisor(cc_list: list[str], to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list)) for coro in to_do_iter: try: - res = await coro + status = await coro except FetchError as exc: country_code = exc.country_code try: @@ -104,9 +105,7 @@ async def supervisor(cc_list: list[str], error_msg = 'Unknown cause' if verbose and error_msg: print(f'*** Error for {country_code}: {error_msg}') - status = HTTPStatus.error - else: - status = res.status + status = DownloadStatus.ERROR counter[status] += 1 @@ -116,7 +115,7 @@ async def supervisor(cc_list: list[str], def download_many(cc_list: list[str], base_url: str, verbose: bool, - concur_req: int) -> Counter[HTTPStatus]: + concur_req: int) -> Counter[DownloadStatus]: coro = supervisor(cc_list, base_url, verbose, concur_req) counts = asyncio.run(coro) # <14> diff --git a/20-futures/getflags/flags_asyncio.py b/20-futures/getflags/flags_asyncio.py index ee431d0..ba28065 100755 --- a/20-futures/getflags/flags_asyncio.py +++ b/20-futures/getflags/flags_asyncio.py @@ -9,38 +9,38 @@ $ python3 flags_asyncio.py EG VN IN TR RU ID US DE CN MX JP BD NG ET FR BR PH PK CD IR 20 flags downloaded in 1.07s - """ # tag::FLAGS_ASYNCIO_TOP[] import asyncio -from aiohttp import ClientSession # <1> +from httpx import AsyncClient # <1> from flags import BASE_URL, save_flag, main # <2> -async def download_one(session: ClientSession, cc: str): # <3> +async def download_one(session: AsyncClient, cc: str): # <3> image = await get_flag(session, cc) save_flag(image, f'{cc}.gif') print(cc, end=' ', flush=True) return cc -async def get_flag(session: ClientSession, cc: str) -> bytes: # <4> +async def get_flag(session: AsyncClient, cc: str) -> bytes: # <4> url = f'{BASE_URL}/{cc}/{cc}.gif'.lower() - async with session.get(url) as resp: # <5> - return await resp.read() # <6> + resp = await session.get(url, timeout=6.1, + follow_redirects=True) # <5> + return resp.read() # <6> # end::FLAGS_ASYNCIO_TOP[] # tag::FLAGS_ASYNCIO_START[] -def download_many(cc_list: list[str]) -> int: # <1> - return asyncio.run(supervisor(cc_list)) # <2> +def download_many(cc_list: list[str]) -> int: # <1> + return asyncio.run(supervisor(cc_list)) # <2> async def supervisor(cc_list: list[str]) -> int: - async with ClientSession() as session: # <3> - to_do = [download_one(session, cc) # <4> - for cc in sorted(cc_list)] - res = await asyncio.gather(*to_do) # <5> + async with AsyncClient() as session: # <3> + to_do = [download_one(session, cc) + for cc in sorted(cc_list)] # <4> + res = await asyncio.gather(*to_do) # <5> - return len(res) # <6> + return len(res) # <6> if __name__ == '__main__': main(download_many) diff --git a/20-futures/getflags/requirements.txt b/20-futures/getflags/requirements.txt index 37baa8d..b8bb630 100644 --- a/20-futures/getflags/requirements.txt +++ b/20-futures/getflags/requirements.txt @@ -1,13 +1,10 @@ -aiohttp==3.7.4.post0 -async-timeout==3.0.1 -attrs==21.2.0 +anyio==3.3.2 certifi==2021.5.30 -chardet==4.0.0 -charset-normalizer==2.0.4 +charset-normalizer==2.0.6 +h11==0.12.0 +httpcore==0.13.7 +httpx==1.0.0b0 idna==3.2 -multidict==5.1.0 -requests==2.26.0 -tqdm==4.62.2 -typing-extensions==3.10.0.2 -urllib3==1.26.6 -yarl==1.6.3 +rfc3986==1.5.0 +sniffio==1.2.0 +tqdm==4.62.3 diff --git a/20-futures/getflags/slow_server.py b/20-futures/getflags/slow_server.py index bede010..90e6672 100755 --- a/20-futures/getflags/slow_server.py +++ b/20-futures/getflags/slow_server.py @@ -15,8 +15,10 @@ from functools import partial from http import server, HTTPStatus from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler -from random import random +from random import random, uniform +MIN_DELAY = 0.5 # minimum delay for do_GET (seconds) +MAX_DELAY = 5.0 # maximum delay for do_GET (seconds) class SlowHTTPRequestHandler(SimpleHTTPRequestHandler): """SlowHTTPRequestHandler adds delays and errors to test HTTP clients. @@ -36,15 +38,23 @@ def __init__(self, *args, error_rate=0.0, **kwargs): def do_GET(self): """Serve a GET request.""" - time.sleep(.5) + delay = uniform(MIN_DELAY, MAX_DELAY) + cc = self.path[-6:-4].upper() + print(f'{cc} delay: {delay:0.2}s') + time.sleep(delay) if random() < self.error_rate: # HTTPStatus.IM_A_TEAPOT requires Python >= 3.9 - self.send_error(HTTPStatus.IM_A_TEAPOT, "I'm a Teapot") + try: + self.send_error(HTTPStatus.IM_A_TEAPOT, "I'm a Teapot") + except BrokenPipeError as exc: + print(f'{cc} *** BrokenPipeError: client closed') else: f = self.send_head() if f: try: self.copyfile(f, self.wfile) + except BrokenPipeError as exc: + print(f'{cc} *** BrokenPipeError: client closed') finally: f.close() @@ -67,9 +77,9 @@ def do_GET(self): help='Error rate; e.g. use .25 for 25%% probability ' '[default:0.0]') parser.add_argument('port', action='store', - default=8000, type=int, + default=8001, type=int, nargs='?', - help='Specify alternate port [default: 8000]') + help='Specify alternate port [default: 8001]') args = parser.parse_args() handler_class = partial(SlowHTTPRequestHandler, directory=args.directory, diff --git a/20-futures/getflags/tree.py b/20-futures/getflags/tree.py new file mode 100644 index 0000000..4f65697 --- /dev/null +++ b/20-futures/getflags/tree.py @@ -0,0 +1,38 @@ +import httpx + +def tree(cls, level=0): + yield cls.__name__, level + for sub_cls in cls.__subclasses__(): + yield from tree(sub_cls, level+1) + + +def display(cls): + for cls_name, level in tree(cls): + indent = ' ' * 4 * level + print(f'{indent}{cls_name}') + + +def find_roots(module): + exceptions = [] + for name in dir(module): + obj = getattr(module, name) + if isinstance(obj, type) and issubclass(obj, BaseException): + exceptions.append(obj) + roots = [] + for exc in exceptions: + root = True + for other in exceptions: + if exc is not other and issubclass(exc, other): + root = False + break + if root: + roots.append(exc) + return roots + + +def main(): + for exc in find_roots(httpx): + display(exc) + +if __name__ == '__main__': + main() From 0f91193c9f0cc6cf200c22e3828f2599fa8a67ed Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 2 Oct 2021 17:13:14 -0300 Subject: [PATCH 081/127] sequential, threaded & async HTTP clients using HTTPX --- 20-futures/getflags/flags3_asyncio.py | 126 -------------------------- 1 file changed, 126 deletions(-) delete mode 100755 20-futures/getflags/flags3_asyncio.py diff --git a/20-futures/getflags/flags3_asyncio.py b/20-futures/getflags/flags3_asyncio.py deleted file mode 100755 index b698d4d..0000000 --- a/20-futures/getflags/flags3_asyncio.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python3 - -"""Download flags of countries (with error handling). - -asyncio async/await version using run_in_executor for save_flag. - -""" - -import asyncio -from collections import Counter -from http import HTTPStatus - -import aiohttp -import tqdm # type: ignore - -from flags2_common import main, DownloadStatus, save_flag - -# default set low to avoid errors from remote site, such as -# 503 - Service Temporarily Unavailable -DEFAULT_CONCUR_REQ = 5 -MAX_CONCUR_REQ = 1000 - - -class FetchError(Exception): - def __init__(self, country_code: str): - self.country_code = country_code - -async def get_flag(session: aiohttp.ClientSession, - base_url: str, - cc: str) -> bytes: - url = f'{base_url}/{cc}/{cc}.gif' - async with session.get(url) as resp: - if resp.status == 200: - return await resp.read() - else: - resp.raise_for_status() - return bytes() - -# tag::FLAGS3_ASYNCIO_GET_COUNTRY[] -async def get_country(session: aiohttp.ClientSession, - base_url: str, - cc: str) -> str: # <1> - url = f'{base_url}/{cc}/metadata.json' - async with session.get(url) as resp: - if resp.status == 200: - metadata = await resp.json() # <2> - return metadata.get('country', 'no name') # <3> - else: - resp.raise_for_status() - return '' -# end::FLAGS3_ASYNCIO_GET_COUNTRY[] - -# tag::FLAGS3_ASYNCIO_DOWNLOAD_ONE[] -async def download_one(session: aiohttp.ClientSession, - cc: str, - base_url: str, - semaphore: asyncio.Semaphore, - verbose: bool) -> DownloadStatus: - try: - async with semaphore: - image = await get_flag(session, base_url, cc) # <1> - async with semaphore: - country = await get_country(session, base_url, cc) # <2> - except aiohttp.ClientResponseError as exc: - if exc.status == HTTPStatus.NOT_FOUND: - status = DownloadStatus.NOT_FOUND - msg = 'not found' - else: - raise FetchError(cc) from exc - else: - filename = country.replace(' ', '_') # <3> - filename = f'{filename}.gif' - loop = asyncio.get_running_loop() - loop.run_in_executor(None, - save_flag, image, filename) - status = DownloadStatus.OK - msg = 'OK' - if verbose and msg: - print(cc, msg) - return status -# end::FLAGS3_ASYNCIO_DOWNLOAD_ONE[] - -async def supervisor(cc_list: list[str], - base_url: str, - verbose: bool, - concur_req: int) -> Counter[DownloadStatus]: - counter: Counter[DownloadStatus] = Counter() - semaphore = asyncio.Semaphore(concur_req) - async with aiohttp.ClientSession() as session: - to_do = [download_one(session, cc, base_url, - semaphore, verbose) - for cc in sorted(cc_list)] - - to_do_iter = asyncio.as_completed(to_do) - if not verbose: - to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list)) - for coro in to_do_iter: - try: - status = await coro - except FetchError as exc: - country_code = exc.country_code - try: - error_msg = exc.__cause__.message # type: ignore - except AttributeError: - error_msg = 'Unknown cause' - if verbose and error_msg: - print(f'*** Error for {country_code}: {error_msg}') - status = DownloadStatus.ERROR - - counter[status] += 1 - - return counter - - -def download_many(cc_list: list[str], - base_url: str, - verbose: bool, - concur_req: int) -> Counter[DownloadStatus]: - coro = supervisor(cc_list, base_url, verbose, concur_req) - counts = asyncio.run(coro) # <14> - - return counts - - -if __name__ == '__main__': - main(download_many, DEFAULT_CONCUR_REQ, MAX_CONCUR_REQ) From 6359a4de0c7f3b155b5445512fe99972ed7c5f43 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 2 Oct 2021 17:27:22 -0300 Subject: [PATCH 082/127] sequential, threaded & async HTTP clients using HTTPX --- 20-futures/getflags/downloaded/.gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 20-futures/getflags/downloaded/.gitignore diff --git a/20-futures/getflags/downloaded/.gitignore b/20-futures/getflags/downloaded/.gitignore deleted file mode 100644 index d8c78e1..0000000 --- a/20-futures/getflags/downloaded/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.gif \ No newline at end of file From 980f75032685921e08d5200513a261e83ef5f858 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 2 Oct 2021 17:48:34 -0300 Subject: [PATCH 083/127] sequential, threaded & async HTTP clients using HTTPX --- 20-futures/getflags/flags2_asyncio.py | 1 + 20-futures/getflags/flags2_asyncio_executor.py | 1 + 2 files changed, 2 insertions(+) diff --git a/20-futures/getflags/flags2_asyncio.py b/20-futures/getflags/flags2_asyncio.py index a2bb6ff..4825db4 100755 --- a/20-futures/getflags/flags2_asyncio.py +++ b/20-futures/getflags/flags2_asyncio.py @@ -67,6 +67,7 @@ async def supervisor(cc_list: list[str], to_do_iter = asyncio.as_completed(to_do) # <4> if not verbose: to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list)) # <5> + error: httpx.HTTPError | None = None for coro in to_do_iter: # <6> try: status = await coro # <7> diff --git a/20-futures/getflags/flags2_asyncio_executor.py b/20-futures/getflags/flags2_asyncio_executor.py index 7143b46..752b60f 100755 --- a/20-futures/getflags/flags2_asyncio_executor.py +++ b/20-futures/getflags/flags2_asyncio_executor.py @@ -70,6 +70,7 @@ async def supervisor(cc_list: list[str], to_do_iter = asyncio.as_completed(to_do) # <4> if not verbose: to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list)) # <5> + error: httpx.HTTPError | None = None for coro in to_do_iter: # <6> try: status = await coro # <7> From 5d6b15604782c715c85236d6de30b90add3f8d32 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Tue, 5 Oct 2021 09:44:12 -0300 Subject: [PATCH 084/127] ch20: renamed directory --- {20-futures => 20-executors}/demo_executor_map.py | 0 {20-futures => 20-executors}/getflags/.gitignore | 0 {20-futures => 20-executors}/getflags/README.adoc | 0 .../getflags/country_codes.txt | 0 {20-futures => 20-executors}/getflags/flags.py | 0 {20-futures => 20-executors}/getflags/flags.zip | Bin .../getflags/flags2_asyncio.py | 0 .../getflags/flags2_asyncio_executor.py | 0 .../getflags/flags2_common.py | 0 .../getflags/flags2_sequential.py | 0 .../getflags/flags2_threadpool.py | 0 .../getflags/flags_asyncio.py | 0 .../getflags/flags_threadpool.py | 0 .../getflags/flags_threadpool_futures.py | 0 .../getflags/requirements.txt | 0 .../getflags/slow_server.py | 0 {20-futures => 20-executors}/getflags/tree.py | 0 {20-futures => 20-executors}/primes/primes.py | 0 {20-futures => 20-executors}/primes/proc_pool.py | 0 19 files changed, 0 insertions(+), 0 deletions(-) rename {20-futures => 20-executors}/demo_executor_map.py (100%) rename {20-futures => 20-executors}/getflags/.gitignore (100%) rename {20-futures => 20-executors}/getflags/README.adoc (100%) rename {20-futures => 20-executors}/getflags/country_codes.txt (100%) rename {20-futures => 20-executors}/getflags/flags.py (100%) rename {20-futures => 20-executors}/getflags/flags.zip (100%) rename {20-futures => 20-executors}/getflags/flags2_asyncio.py (100%) rename {20-futures => 20-executors}/getflags/flags2_asyncio_executor.py (100%) rename {20-futures => 20-executors}/getflags/flags2_common.py (100%) rename {20-futures => 20-executors}/getflags/flags2_sequential.py (100%) rename {20-futures => 20-executors}/getflags/flags2_threadpool.py (100%) rename {20-futures => 20-executors}/getflags/flags_asyncio.py (100%) rename {20-futures => 20-executors}/getflags/flags_threadpool.py (100%) rename {20-futures => 20-executors}/getflags/flags_threadpool_futures.py (100%) rename {20-futures => 20-executors}/getflags/requirements.txt (100%) rename {20-futures => 20-executors}/getflags/slow_server.py (100%) rename {20-futures => 20-executors}/getflags/tree.py (100%) rename {20-futures => 20-executors}/primes/primes.py (100%) rename {20-futures => 20-executors}/primes/proc_pool.py (100%) diff --git a/20-futures/demo_executor_map.py b/20-executors/demo_executor_map.py similarity index 100% rename from 20-futures/demo_executor_map.py rename to 20-executors/demo_executor_map.py diff --git a/20-futures/getflags/.gitignore b/20-executors/getflags/.gitignore similarity index 100% rename from 20-futures/getflags/.gitignore rename to 20-executors/getflags/.gitignore diff --git a/20-futures/getflags/README.adoc b/20-executors/getflags/README.adoc similarity index 100% rename from 20-futures/getflags/README.adoc rename to 20-executors/getflags/README.adoc diff --git a/20-futures/getflags/country_codes.txt b/20-executors/getflags/country_codes.txt similarity index 100% rename from 20-futures/getflags/country_codes.txt rename to 20-executors/getflags/country_codes.txt diff --git a/20-futures/getflags/flags.py b/20-executors/getflags/flags.py similarity index 100% rename from 20-futures/getflags/flags.py rename to 20-executors/getflags/flags.py diff --git a/20-futures/getflags/flags.zip b/20-executors/getflags/flags.zip similarity index 100% rename from 20-futures/getflags/flags.zip rename to 20-executors/getflags/flags.zip diff --git a/20-futures/getflags/flags2_asyncio.py b/20-executors/getflags/flags2_asyncio.py similarity index 100% rename from 20-futures/getflags/flags2_asyncio.py rename to 20-executors/getflags/flags2_asyncio.py diff --git a/20-futures/getflags/flags2_asyncio_executor.py b/20-executors/getflags/flags2_asyncio_executor.py similarity index 100% rename from 20-futures/getflags/flags2_asyncio_executor.py rename to 20-executors/getflags/flags2_asyncio_executor.py diff --git a/20-futures/getflags/flags2_common.py b/20-executors/getflags/flags2_common.py similarity index 100% rename from 20-futures/getflags/flags2_common.py rename to 20-executors/getflags/flags2_common.py diff --git a/20-futures/getflags/flags2_sequential.py b/20-executors/getflags/flags2_sequential.py similarity index 100% rename from 20-futures/getflags/flags2_sequential.py rename to 20-executors/getflags/flags2_sequential.py diff --git a/20-futures/getflags/flags2_threadpool.py b/20-executors/getflags/flags2_threadpool.py similarity index 100% rename from 20-futures/getflags/flags2_threadpool.py rename to 20-executors/getflags/flags2_threadpool.py diff --git a/20-futures/getflags/flags_asyncio.py b/20-executors/getflags/flags_asyncio.py similarity index 100% rename from 20-futures/getflags/flags_asyncio.py rename to 20-executors/getflags/flags_asyncio.py diff --git a/20-futures/getflags/flags_threadpool.py b/20-executors/getflags/flags_threadpool.py similarity index 100% rename from 20-futures/getflags/flags_threadpool.py rename to 20-executors/getflags/flags_threadpool.py diff --git a/20-futures/getflags/flags_threadpool_futures.py b/20-executors/getflags/flags_threadpool_futures.py similarity index 100% rename from 20-futures/getflags/flags_threadpool_futures.py rename to 20-executors/getflags/flags_threadpool_futures.py diff --git a/20-futures/getflags/requirements.txt b/20-executors/getflags/requirements.txt similarity index 100% rename from 20-futures/getflags/requirements.txt rename to 20-executors/getflags/requirements.txt diff --git a/20-futures/getflags/slow_server.py b/20-executors/getflags/slow_server.py similarity index 100% rename from 20-futures/getflags/slow_server.py rename to 20-executors/getflags/slow_server.py diff --git a/20-futures/getflags/tree.py b/20-executors/getflags/tree.py similarity index 100% rename from 20-futures/getflags/tree.py rename to 20-executors/getflags/tree.py diff --git a/20-futures/primes/primes.py b/20-executors/primes/primes.py similarity index 100% rename from 20-futures/primes/primes.py rename to 20-executors/primes/primes.py diff --git a/20-futures/primes/proc_pool.py b/20-executors/primes/proc_pool.py similarity index 100% rename from 20-futures/primes/proc_pool.py rename to 20-executors/primes/proc_pool.py From 43f1bf23b3ba020597b455e740fe553f1923fca9 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Tue, 5 Oct 2021 09:52:43 -0300 Subject: [PATCH 085/127] sync from Atlas --- 19-concurrency/primes/procs.py | 60 +++++++------- 19-concurrency/primes/procs_race_condition.py | 80 +++++++++++++++++++ 20-executors/getflags/.gitignore | 3 +- 20-executors/getflags/flags2_threadpool.py | 10 +-- README.md | 2 +- 5 files changed, 118 insertions(+), 37 deletions(-) create mode 100755 19-concurrency/primes/procs_race_condition.py diff --git a/19-concurrency/primes/procs.py b/19-concurrency/primes/procs.py index 09f75e3..50e3d9f 100644 --- a/19-concurrency/primes/procs.py +++ b/19-concurrency/primes/procs.py @@ -31,46 +31,46 @@ def worker(jobs: JobQueue, results: ResultQueue) -> None: # <7> while n := jobs.get(): # <8> results.put(check(n)) # <9> results.put(PrimeResult(0, False, 0.0)) # <10> -# end::PRIMES_PROC_TOP[] -# tag::PRIMES_PROC_MIDDLE[] -def start_jobs(workers: int, jobs: JobQueue, results: ResultQueue) -> None: +def start_jobs( + procs: int, jobs: JobQueue, results: ResultQueue # <11> +) -> None: for n in NUMBERS: - jobs.put(n) # <1> - for _ in range(workers): - proc = Process(target=worker, args=(jobs, results)) # <2> - proc.start() # <3> - jobs.put(0) # <4> - -def report(workers: int, results: ResultQueue) -> int: - checked = 0 - workers_done = 0 - while workers_done < workers: - n, prime, elapsed = results.get() - if n == 0: - workers_done += 1 - else: - checked += 1 - label = 'P' if prime else ' ' - print(f'{n:16} {label} {elapsed:9.6f}s') - return checked -# end::PRIMES_PROC_MIDDLE[] + jobs.put(n) # <12> + for _ in range(procs): + proc = Process(target=worker, args=(jobs, results)) # <13> + proc.start() # <14> + jobs.put(0) # <15> +# end::PRIMES_PROC_TOP[] # tag::PRIMES_PROC_MAIN[] def main() -> None: - if len(sys.argv) < 2: - workers = cpu_count() + if len(sys.argv) < 2: # <1> + procs = cpu_count() else: - workers = int(sys.argv[1]) + procs = int(sys.argv[1]) - print(f'Checking {len(NUMBERS)} numbers with {workers} processes:') + print(f'Checking {len(NUMBERS)} numbers with {procs} processes:') t0 = perf_counter() - jobs: JobQueue = SimpleQueue() + jobs: JobQueue = SimpleQueue() # <2> results: ResultQueue = SimpleQueue() - start_jobs(workers, jobs, results) - checked = report(workers, results) + start_jobs(procs, jobs, results) # <3> + checked = report(procs, results) # <4> elapsed = perf_counter() - t0 - print(f'{checked} checks in {elapsed:.2f}s') + print(f'{checked} checks in {elapsed:.2f}s') # <5> + +def report(procs: int, results: ResultQueue) -> int: # <6> + checked = 0 + procs_done = 0 + while procs_done < procs: # <7> + n, prime, elapsed = results.get() # <8> + if n == 0: # <9> + procs_done += 1 + else: + checked += 1 # <10> + label = 'P' if prime else ' ' + print(f'{n:16} {label} {elapsed:9.6f}s') + return checked if __name__ == '__main__': main() diff --git a/19-concurrency/primes/procs_race_condition.py b/19-concurrency/primes/procs_race_condition.py new file mode 100755 index 0000000..aa5e172 --- /dev/null +++ b/19-concurrency/primes/procs_race_condition.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +""" +procs.py: shows that multiprocessing on a multicore machine +can be faster than sequential code for CPU-intensive work. +""" + +# tag::PRIMES_PROC_TOP[] +import sys +from time import perf_counter +from typing import NamedTuple +from multiprocessing import Process, SimpleQueue, cpu_count # <1> +from multiprocessing import queues # <2> + +from primes import is_prime, NUMBERS + +class PrimeResult(NamedTuple): # <3> + n: int + prime: bool + elapsed: float + +JobQueue = queues.SimpleQueue[int] # <4> +ResultQueue = queues.SimpleQueue[PrimeResult] # <5> + +def check(n: int) -> PrimeResult: # <6> + t0 = perf_counter() + res = is_prime(n) + return PrimeResult(n, res, perf_counter() - t0) + +def worker(jobs: JobQueue, results: ResultQueue) -> None: # <7> + while n := jobs.get(): # <8> + results.put(check(n)) # <9> + results.put(PrimeResult(0, False, 0.0)) +# end::PRIMES_PROC_TOP[] + +def start_jobs(workers: int) -> ResultQueue: + jobs: JobQueue = SimpleQueue() # <2> + results: ResultQueue = SimpleQueue() + + for n in NUMBERS: # <3> + jobs.put(n) + + for _ in range(workers): + proc = Process(target=worker, args=(jobs, results)) # <4> + proc.start() # <5> + jobs.put(0) # <6> + + return results + +def report(workers: int, results: ResultQueue) -> int: + workers_done = 0 + checked = 0 + while workers_done < workers: + n, prime, elapsed = results.get() # <7> + if n == 0: + workers_done += 1 + else: + checked += 1 + label = 'P' if prime else ' ' + print(f'{n:16} {label} {elapsed:9.6f}s') # <8> + return checked + + +# tag::PRIMES_PROC_MAIN[] +def main() -> None: + if len(sys.argv) < 2: # <1> + workers = cpu_count() + else: + workers = int(sys.argv[1]) + + print(f'Checking {len(NUMBERS)} numbers with {workers} processes:') + t0 = perf_counter() + results = start_jobs(workers) + checked = report(workers, results) + elapsed = perf_counter() - t0 + print(f'{checked} checks in {elapsed:.2f}s') + +if __name__ == '__main__': + main() +# end::PRIMES_PROC_MAIN[] diff --git a/20-executors/getflags/.gitignore b/20-executors/getflags/.gitignore index 8484300..aad8463 100644 --- a/20-executors/getflags/.gitignore +++ b/20-executors/getflags/.gitignore @@ -1 +1,2 @@ -flags/ \ No newline at end of file +flags/ +downloaded/ diff --git a/20-executors/getflags/flags2_threadpool.py b/20-executors/getflags/flags2_threadpool.py index 8955c7c..62f6c3b 100755 --- a/20-executors/getflags/flags2_threadpool.py +++ b/20-executors/getflags/flags2_threadpool.py @@ -20,7 +20,7 @@ # tag::FLAGS2_THREADPOOL[] from collections import Counter -from concurrent import futures +from concurrent.futures import ThreadPoolExecutor, as_completed import httpx import tqdm # type: ignore @@ -37,13 +37,13 @@ def download_many(cc_list: list[str], verbose: bool, concur_req: int) -> Counter[DownloadStatus]: counter: Counter[DownloadStatus] = Counter() - with futures.ThreadPoolExecutor(max_workers=concur_req) as executor: # <4> + with ThreadPoolExecutor(max_workers=concur_req) as executor: # <4> to_do_map = {} # <5> for cc in sorted(cc_list): # <6> future = executor.submit(download_one, cc, base_url, verbose) # <7> to_do_map[future] = cc # <8> - done_iter = futures.as_completed(to_do_map) # <9> + done_iter = as_completed(to_do_map) # <9> if not verbose: done_iter = tqdm.tqdm(done_iter, total=len(cc_list)) # <10> for future in done_iter: # <11> @@ -52,7 +52,7 @@ def download_many(cc_list: list[str], except httpx.HTTPStatusError as exc: # <13> error_msg = 'HTTP error {resp.status_code} - {resp.reason_phrase}' error_msg = error_msg.format(resp=exc.response) - except httpx.RequestError as exc: # <15> + except httpx.RequestError as exc: error_msg = f'{exc} {type(exc)}'.strip() except KeyboardInterrupt: break @@ -63,7 +63,7 @@ def download_many(cc_list: list[str], status = DownloadStatus.ERROR counter[status] += 1 if verbose and error_msg: - cc = to_do_map[future] # <16> + cc = to_do_map[future] # <14> print(f'{cc} error: {error_msg}') return counter diff --git a/README.md b/README.md index f0854e9..96273ac 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Part / Chapter #|Title|Directory|1st ed. Chapter # 17|Iterators, Generators, and Classic Coroutines|[17-it-generator](17-it-generator)|14 18|Context Managers and else Blocks|[18-with-match](18-with-match)|15 19|Concurrency Models in Python|[19-concurrency](19-concurrency)|🆕 -20|Concurrency with Futures|[20-futures](20-futures)|17 +20|Concurrent Executors|[20-executors](20-executors)|17 21|Asynchronous Programming|[21-async](21-async)|18 **VI – Metaprogramming**| 22|Dynamic Attributes and Properties|[22-dyn-attr-prop](22-dyn-attr-prop)|19 From 6fb0832c4041ec8f46f80d0a8bac5af5a69f32f4 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Tue, 12 Oct 2021 22:37:28 -0300 Subject: [PATCH 086/127] sync from Atlas --- 02-array-seq/lispy/py3.10/examples_test.py | 2 +- 02-array-seq/lispy/py3.9/lis.py | 2 +- 18-with-match/lispy/py3.10/examples_test.py | 2 +- 18-with-match/lispy/py3.10/lis.py | 2 +- 18-with-match/lispy/py3.9/examples_test.py | 4 +- 18-with-match/lispy/py3.9/lis.py | 2 +- 20-executors/getflags/flags2_asyncio.py | 51 ++++++++++----------- 7 files changed, 31 insertions(+), 34 deletions(-) diff --git a/02-array-seq/lispy/py3.10/examples_test.py b/02-array-seq/lispy/py3.10/examples_test.py index ba6bd10..603ccd3 100644 --- a/02-array-seq/lispy/py3.10/examples_test.py +++ b/02-array-seq/lispy/py3.10/examples_test.py @@ -147,7 +147,7 @@ def test_factorial(): gcd_src = """ (define (mod m n) - (- m (* n (// m n)))) + (- m (* n (quotient m n)))) (define (gcd m n) (if (= n 0) m diff --git a/02-array-seq/lispy/py3.9/lis.py b/02-array-seq/lispy/py3.9/lis.py index 201b41e..dd80096 100644 --- a/02-array-seq/lispy/py3.9/lis.py +++ b/02-array-seq/lispy/py3.9/lis.py @@ -80,7 +80,7 @@ def standard_env() -> Environment: '-': op.sub, '*': op.mul, '/': op.truediv, - '//': op.floordiv, + 'quotient': op.floordiv, '>': op.gt, '<': op.lt, '>=': op.ge, diff --git a/18-with-match/lispy/py3.10/examples_test.py b/18-with-match/lispy/py3.10/examples_test.py index 2807596..d207df0 100644 --- a/18-with-match/lispy/py3.10/examples_test.py +++ b/18-with-match/lispy/py3.10/examples_test.py @@ -146,7 +146,7 @@ def test_factorial(): gcd_src = """ (define (mod m n) - (- m (* n (// m n)))) + (- m (* n (quotient m n)))) (define (gcd m n) (if (= n 0) m diff --git a/18-with-match/lispy/py3.10/lis.py b/18-with-match/lispy/py3.10/lis.py index 0faf6d2..04cc28d 100755 --- a/18-with-match/lispy/py3.10/lis.py +++ b/18-with-match/lispy/py3.10/lis.py @@ -83,7 +83,7 @@ def standard_env() -> Environment: '-': op.sub, '*': op.mul, '/': op.truediv, - '//': op.floordiv, + 'quotient': op.floordiv, '>': op.gt, '<': op.lt, '>=': op.ge, diff --git a/18-with-match/lispy/py3.9/examples_test.py b/18-with-match/lispy/py3.9/examples_test.py index c632662..603ccd3 100644 --- a/18-with-match/lispy/py3.9/examples_test.py +++ b/18-with-match/lispy/py3.9/examples_test.py @@ -147,7 +147,7 @@ def test_factorial(): gcd_src = """ (define (mod m n) - (- m (* n (// m n)))) + (- m (* n (quotient m n)))) (define (gcd m n) (if (= n 0) m @@ -255,4 +255,4 @@ def test_closure_with_change(capsys): def test_closure_averager(): got = run(closure_averager_src) assert got == 12.0 -# end::RUN_AVERAGER[] \ No newline at end of file +# end::RUN_AVERAGER[] diff --git a/18-with-match/lispy/py3.9/lis.py b/18-with-match/lispy/py3.9/lis.py index 6ab8a19..e868a14 100644 --- a/18-with-match/lispy/py3.9/lis.py +++ b/18-with-match/lispy/py3.9/lis.py @@ -80,7 +80,7 @@ def standard_env() -> Environment: '-': op.sub, '*': op.mul, '/': op.truediv, - '//': op.floordiv, + 'quotient': op.floordiv, '>': op.gt, '<': op.lt, '>=': op.ge, diff --git a/20-executors/getflags/flags2_asyncio.py b/20-executors/getflags/flags2_asyncio.py index 4825db4..a922db5 100755 --- a/20-executors/getflags/flags2_asyncio.py +++ b/20-executors/getflags/flags2_asyncio.py @@ -16,37 +16,36 @@ from flags2_common import main, DownloadStatus, save_flag -# default set low to avoid errors from remote site, such as -# 503 - Service Temporarily Unavailable +# low concurrency default to avoid errors from remote site, +# such as 503 - Service Temporarily Unavailable DEFAULT_CONCUR_REQ = 5 MAX_CONCUR_REQ = 1000 -async def get_flag(session: httpx.AsyncClient, # <2> +async def get_flag(client: httpx.AsyncClient, # <1> base_url: str, cc: str) -> bytes: url = f'{base_url}/{cc}/{cc}.gif'.lower() - resp = await session.get(url, timeout=3.1, follow_redirects=True) # <3> + resp = await client.get(url, timeout=3.1, follow_redirects=True) # <2> resp.raise_for_status() return resp.content -async def download_one(session: httpx.AsyncClient, +async def download_one(client: httpx.AsyncClient, cc: str, base_url: str, - semaphore: asyncio.Semaphore, # <4> + semaphore: asyncio.Semaphore, verbose: bool) -> DownloadStatus: try: - async with semaphore: # <5> - image = await get_flag(session, base_url, cc) - except httpx.HTTPStatusError as exc: # <4> + async with semaphore: # <3> + image = await get_flag(client, base_url, cc) + except httpx.HTTPStatusError as exc: # <5> res = exc.response if res.status_code == HTTPStatus.NOT_FOUND: - status = DownloadStatus.NOT_FOUND # <5> + status = DownloadStatus.NOT_FOUND msg = f'not found: {res.url}' else: raise - else: - await asyncio.to_thread(save_flag, image, f'{cc}.gif') + await asyncio.to_thread(save_flag, image, f'{cc}.gif') # <6> status = DownloadStatus.OK msg = 'OK' if verbose and msg: @@ -61,33 +60,31 @@ async def supervisor(cc_list: list[str], concur_req: int) -> Counter[DownloadStatus]: # <1> counter: Counter[DownloadStatus] = Counter() semaphore = asyncio.Semaphore(concur_req) # <2> - async with httpx.AsyncClient() as session: - to_do = [download_one(session, cc, base_url, semaphore, verbose) + async with httpx.AsyncClient() as client: + to_do = [download_one(client, cc, base_url, semaphore, verbose) for cc in sorted(cc_list)] # <3> to_do_iter = asyncio.as_completed(to_do) # <4> if not verbose: to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list)) # <5> - error: httpx.HTTPError | None = None - for coro in to_do_iter: # <6> + error: httpx.HTTPError | None = None # <6> + for coro in to_do_iter: # <7> try: - status = await coro # <7> - except httpx.HTTPStatusError as exc: # <8> + status = await coro # <8> + except httpx.HTTPStatusError as exc: error_msg = 'HTTP error {resp.status_code} - {resp.reason_phrase}' error_msg = error_msg.format(resp=exc.response) - error = exc - except httpx.RequestError as exc: # <9> + error = exc # <9> + except httpx.RequestError as exc: error_msg = f'{exc} {type(exc)}'.strip() - error = exc - except KeyboardInterrupt: # <10> + error = exc # <10> + except KeyboardInterrupt: break - else: # <11> - error = None if error: - status = DownloadStatus.ERROR # <12> + status = DownloadStatus.ERROR # <11> if verbose: - url = str(error.request.url) # <13> - cc = Path(url).stem.upper() # <14> + url = str(error.request.url) # <12> + cc = Path(url).stem.upper() # <13> print(f'{cc} error: {error_msg}') counter[status] += 1 From f1524171df7fd44204d33212b4af3e86b58a212a Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 18 Oct 2021 14:42:55 -0300 Subject: [PATCH 087/127] refactored drawtree.py --- 17-it-generator/tree/extra/drawtree.py | 28 +++++++++++++++ 17-it-generator/tree/extra/pretty_tree.py | 35 ------------------- .../{test_pretty_tree.py => test_drawtree.py} | 6 ++-- 3 files changed, 31 insertions(+), 38 deletions(-) create mode 100644 17-it-generator/tree/extra/drawtree.py delete mode 100644 17-it-generator/tree/extra/pretty_tree.py rename 17-it-generator/tree/extra/{test_pretty_tree.py => test_drawtree.py} (94%) diff --git a/17-it-generator/tree/extra/drawtree.py b/17-it-generator/tree/extra/drawtree.py new file mode 100644 index 0000000..043e53b --- /dev/null +++ b/17-it-generator/tree/extra/drawtree.py @@ -0,0 +1,28 @@ +from tree import tree + +SP = '\N{SPACE}' +HLIN = '\N{BOX DRAWINGS LIGHT HORIZONTAL}' # ─ +ELBOW = f'\N{BOX DRAWINGS LIGHT UP AND RIGHT}{HLIN*2}{SP}' # └── +TEE = f'\N{BOX DRAWINGS LIGHT VERTICAL AND RIGHT}{HLIN*2}{SP}' # ├── +PIPE = f'\N{BOX DRAWINGS LIGHT VERTICAL}{SP*3}' # │ + + +def render_lines(tree_iter): + name, _, _ = next(tree_iter) + yield name + prefix = '' + + for name, level, last in tree_iter: + prefix = prefix[:4 * (level-1)] + prefix = prefix.replace(TEE, PIPE).replace(ELBOW, SP*4) + prefix += ELBOW if last else TEE + yield prefix + name + + +def draw(cls): + for line in render_lines(tree(cls)): + print(line) + + +if __name__ == '__main__': + draw(BaseException) diff --git a/17-it-generator/tree/extra/pretty_tree.py b/17-it-generator/tree/extra/pretty_tree.py deleted file mode 100644 index 6d6d91a..0000000 --- a/17-it-generator/tree/extra/pretty_tree.py +++ /dev/null @@ -1,35 +0,0 @@ -from tree import tree - -SPACES = ' ' * 4 -HLINE = '\u2500' # ─ BOX DRAWINGS LIGHT HORIZONTAL -HLINE2 = HLINE * 2 -ELBOW = f'\u2514{HLINE2} ' # └ BOX DRAWINGS LIGHT UP AND RIGHT -TEE = f'\u251C{HLINE2} ' # ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT -PIPE = '\u2502 ' # │ BOX DRAWINGS LIGHT VERTICAL - - -def render_lines(tree_iter): - name, _, _ = next(tree_iter) - yield name - prefix = '' - - for name, level, last in tree_iter: - if last: - connector = ELBOW - else: - connector = TEE - - prefix = prefix[:4 * (level-1)] - prefix = prefix.replace(TEE, PIPE).replace(ELBOW, SPACES) - prefix += connector - - yield prefix + name - - -def display(cls): - for line in render_lines(tree(cls)): - print(line) - - -if __name__ == '__main__': - display(BaseException) diff --git a/17-it-generator/tree/extra/test_pretty_tree.py b/17-it-generator/tree/extra/test_drawtree.py similarity index 94% rename from 17-it-generator/tree/extra/test_pretty_tree.py rename to 17-it-generator/tree/extra/test_drawtree.py index 70019a3..f66dfa4 100644 --- a/17-it-generator/tree/extra/test_pretty_tree.py +++ b/17-it-generator/tree/extra/test_drawtree.py @@ -1,4 +1,4 @@ -from pretty_tree import tree, render_lines +from drawtree import tree, render_lines def test_1_level(): result = list(render_lines(tree(BrokenPipeError))) @@ -59,7 +59,7 @@ class Leaf2(Branch): pass assert expected == result -def test_3_levels_2_leaves(): +def test_3_levels_2_leaves_dedent(): class A: pass class B(A): pass class C(B): pass @@ -77,7 +77,7 @@ class E(D): pass assert expected == result -def test_4_levels_4_leaves(): +def test_4_levels_4_leaves_dedent(): class A: pass class B1(A): pass class C1(B1): pass From c41611668dbc9431370b1b61dfdab80497794432 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 18 Oct 2021 14:57:06 -0300 Subject: [PATCH 088/127] refactored drawtree.py --- 17-it-generator/tree/extra/drawtree.py | 8 ++++---- 17-it-generator/tree/extra/test_tree.py | 18 ++++++++++++------ 17-it-generator/tree/extra/tree.py | 8 ++++---- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/17-it-generator/tree/extra/drawtree.py b/17-it-generator/tree/extra/drawtree.py index 043e53b..522666a 100644 --- a/17-it-generator/tree/extra/drawtree.py +++ b/17-it-generator/tree/extra/drawtree.py @@ -8,15 +8,15 @@ def render_lines(tree_iter): - name, _, _ = next(tree_iter) - yield name + cls, _, _ = next(tree_iter) + yield cls.__name__ prefix = '' - for name, level, last in tree_iter: + for cls, level, last in tree_iter: prefix = prefix[:4 * (level-1)] prefix = prefix.replace(TEE, PIPE).replace(ELBOW, SP*4) prefix += ELBOW if last else TEE - yield prefix + name + yield prefix + cls.__name__ def draw(cls): diff --git a/17-it-generator/tree/extra/test_tree.py b/17-it-generator/tree/extra/test_tree.py index 4c6404c..b06cf3b 100644 --- a/17-it-generator/tree/extra/test_tree.py +++ b/17-it-generator/tree/extra/test_tree.py @@ -4,7 +4,8 @@ def test_1_level(): class One: pass expected = [('One', 0, True)] - result = list(tree(One)) + result = [(cls.__name__, level, last) + for cls, level, last in tree(One)] assert expected == result @@ -17,7 +18,8 @@ class Leaf2(Branch): pass ('Leaf1', 1, False), ('Leaf2', 1, True), ] - result = list(tree(Branch)) + result = [(cls.__name__, level, last) + for cls, level, last in tree(Branch)] assert expected == result @@ -30,7 +32,8 @@ class Z(Y): pass ('Y', 1, True), ('Z', 2, True), ] - result = list(tree(X)) + result = [(cls.__name__, level, last) + for cls, level, last in tree(X)] assert expected == result @@ -46,7 +49,8 @@ class Level3(Level2): pass ('Level3', 3, True), ] - result = list(tree(Level0)) + result = [(cls.__name__, level, last) + for cls, level, last in tree(Level0)] assert expected == result @@ -68,7 +72,8 @@ class D2(C1): pass ('C2', 2, True), ] - result = list(tree(A)) + result = [(cls.__name__, level, last) + for cls, level, last in tree(A)] assert expected == result @@ -83,7 +88,8 @@ class Root: pass expected.append((name, level, True)) parent = cls - result = list(tree(Root)) + result = [(cls.__name__, level, last) + for cls, level, last in tree(Root)] assert len(result) == level_count assert result[0] == ('Root', 0, True) assert result[-1] == ('Sub99', 99, True) diff --git a/17-it-generator/tree/extra/tree.py b/17-it-generator/tree/extra/tree.py index 7169a28..aeebd37 100644 --- a/17-it-generator/tree/extra/tree.py +++ b/17-it-generator/tree/extra/tree.py @@ -1,5 +1,5 @@ -def tree(cls, level=0, last_in_level=True): - yield cls.__name__, level, last_in_level +def tree(cls, level=0, last_sibling=True): + yield cls, level, last_sibling subclasses = cls.__subclasses__() if subclasses: last = subclasses[-1] @@ -8,9 +8,9 @@ def tree(cls, level=0, last_in_level=True): def display(cls): - for cls_name, level, _ in tree(cls): + for cls, level, _ in tree(cls): indent = ' ' * 4 * level - print(f'{indent}{cls_name}') + print(f'{indent}{cls.__name__}') if __name__ == '__main__': From 7c155cb33725666349867020ec022b78aa3e531e Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 18 Oct 2021 15:22:46 -0300 Subject: [PATCH 089/127] draw tree of HTTPX exceptions --- .../getflags/httpx-error-tree/drawtree.py | 35 +++++++++++++++++++ .../getflags/httpx-error-tree/tree.py | 24 +++++++++++++ 2 files changed, 59 insertions(+) create mode 100755 20-executors/getflags/httpx-error-tree/drawtree.py create mode 100755 20-executors/getflags/httpx-error-tree/tree.py diff --git a/20-executors/getflags/httpx-error-tree/drawtree.py b/20-executors/getflags/httpx-error-tree/drawtree.py new file mode 100755 index 0000000..f2d9fea --- /dev/null +++ b/20-executors/getflags/httpx-error-tree/drawtree.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +from tree import tree + + +SP = '\N{SPACE}' +HLIN = '\N{BOX DRAWINGS LIGHT HORIZONTAL}' # ─ +ELBOW = f'\N{BOX DRAWINGS LIGHT UP AND RIGHT}{HLIN*2}{SP}' # └── +TEE = f'\N{BOX DRAWINGS LIGHT VERTICAL AND RIGHT}{HLIN*2}{SP}' # ├── +PIPE = f'\N{BOX DRAWINGS LIGHT VERTICAL}{SP*3}' # │ + + +def cls_name(cls): + module = 'builtins.' if cls.__module__ == 'builtins' else '' + return module + cls.__name__ + +def render_lines(tree_iter): + cls, _, _ = next(tree_iter) + yield cls_name(cls) + prefix = '' + + for cls, level, last in tree_iter: + prefix = prefix[:4 * (level-1)] + prefix = prefix.replace(TEE, PIPE).replace(ELBOW, SP*4) + prefix += ELBOW if last else TEE + yield prefix + cls_name(cls) + + +def draw(cls): + for line in render_lines(tree(cls)): + print(line) + + +if __name__ == '__main__': + draw(Exception) diff --git a/20-executors/getflags/httpx-error-tree/tree.py b/20-executors/getflags/httpx-error-tree/tree.py new file mode 100755 index 0000000..9b905f2 --- /dev/null +++ b/20-executors/getflags/httpx-error-tree/tree.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +import httpx # make httpx classes available to .__subclasses__() + + +def tree(cls, level=0, last_sibling=True): + yield cls, level, last_sibling + subclasses = [c for c in cls.__subclasses__() + if c.__module__ == 'httpx' or c is RuntimeError] + if subclasses: + last = subclasses[-1] + for sub_cls in subclasses: + yield from tree(sub_cls, level+1, sub_cls is last) + + +def display(cls): + for cls, level, _ in tree(cls): + indent = ' ' * 4 * level + module = 'builtins.' if cls.__module__ == 'builtins' else '' + print(f'{indent}{module}{cls.__name__}') + + +if __name__ == '__main__': + display(Exception) From ce7d45154ff29048e2002e4f4491f199807e3281 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 18 Oct 2021 16:42:59 -0300 Subject: [PATCH 090/127] draw tree of HTTPX exceptions --- 20-executors/getflags/httpx-error-tree/tree.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/20-executors/getflags/httpx-error-tree/tree.py b/20-executors/getflags/httpx-error-tree/tree.py index 9b905f2..7b82975 100755 --- a/20-executors/getflags/httpx-error-tree/tree.py +++ b/20-executors/getflags/httpx-error-tree/tree.py @@ -5,12 +5,13 @@ def tree(cls, level=0, last_sibling=True): yield cls, level, last_sibling - subclasses = [c for c in cls.__subclasses__() - if c.__module__ == 'httpx' or c is RuntimeError] + # get RuntimeError and exceptions defined in httpx + subclasses = [sub for sub in cls.__subclasses__() + if sub is RuntimeError or sub.__module__ == 'httpx'] if subclasses: last = subclasses[-1] - for sub_cls in subclasses: - yield from tree(sub_cls, level+1, sub_cls is last) + for sub in subclasses: + yield from tree(sub, level+1, sub is last) def display(cls): From 07083b8240c9886bb0b4615c81f4d89ecaede1ba Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 18 Oct 2021 16:45:15 -0300 Subject: [PATCH 091/127] draw tree of HTTPX exceptions --- 20-executors/getflags/httpx-error-tree/tree.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/20-executors/getflags/httpx-error-tree/tree.py b/20-executors/getflags/httpx-error-tree/tree.py index 7b82975..eff7870 100755 --- a/20-executors/getflags/httpx-error-tree/tree.py +++ b/20-executors/getflags/httpx-error-tree/tree.py @@ -10,8 +10,8 @@ def tree(cls, level=0, last_sibling=True): if sub is RuntimeError or sub.__module__ == 'httpx'] if subclasses: last = subclasses[-1] - for sub in subclasses: - yield from tree(sub, level+1, sub is last) + for sub in subclasses: + yield from tree(sub, level+1, sub is last) def display(cls): From f5e3cb8ad32bdf183cee85a17c6105d51538b751 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Fri, 22 Oct 2021 14:59:04 -0300 Subject: [PATCH 092/127] minor refactorings --- 17-it-generator/tree/classtree/classtree.py | 75 ++++++++ .../tree/classtree/classtree_test.py | 181 ++++++++++++++++++ 17-it-generator/tree/extra/drawtree.py | 30 ++- 3 files changed, 279 insertions(+), 7 deletions(-) create mode 100755 17-it-generator/tree/classtree/classtree.py create mode 100644 17-it-generator/tree/classtree/classtree_test.py diff --git a/17-it-generator/tree/classtree/classtree.py b/17-it-generator/tree/classtree/classtree.py new file mode 100755 index 0000000..56bbec3 --- /dev/null +++ b/17-it-generator/tree/classtree/classtree.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 + +from importlib import import_module +import sys + + +SP = '\N{SPACE}' +HLIN = '\N{BOX DRAWINGS LIGHT HORIZONTAL}' * 2 + SP # ── +VLIN = '\N{BOX DRAWINGS LIGHT VERTICAL}' + SP * 3 # │ +TEE = '\N{BOX DRAWINGS LIGHT VERTICAL AND RIGHT}' + HLIN # ├── +ELBOW = '\N{BOX DRAWINGS LIGHT UP AND RIGHT}' + HLIN # └── + + +def subclasses(cls): + try: + return cls.__subclasses__() + except TypeError: # handle the `type` type + return cls.__subclasses__(cls) + + +def tree(cls, level=0, last_sibling=True): + yield cls, level, last_sibling + chidren = subclasses(cls) + if chidren: + last = chidren[-1] + for child in chidren: + yield from tree(child, level + 1, child is last) + + +def render_lines(tree_generator): + cls, _, _ = next(tree_generator) + yield cls.__name__ + prefix = '' + for cls, level, last in tree_generator: + prefix = prefix[: 4 * (level - 1)] + prefix = prefix.replace(TEE, VLIN).replace(ELBOW, SP * 4) + prefix += ELBOW if last else TEE + yield prefix + cls.__name__ + + +def draw(cls): + for line in render_lines(tree(cls)): + print(line) + + +def parse(name): + if '.' in name: + return name.rsplit('.', 1) + else: + return 'builtins', name + + +def main(name): + module_name, cls_name = parse(name) + try: + cls = getattr(import_module(module_name), cls_name) + except ModuleNotFoundError: + print(f'*** Could not import {module_name!r}.') + except AttributeError: + print(f'*** {cls_name!r} not found in {module_name!r}.') + else: + if isinstance(cls, type): + draw(cls) + else: + print(f'*** {cls_name!r} is not a class.') + + +if __name__ == '__main__': + if len(sys.argv) == 2: + main(sys.argv[1]) + else: + print('Usage:' + f'\t{sys.argv[0]} Class # for builtin classes\n' + f'\t{sys.argv[0]} package.Class # for other classes' + ) diff --git a/17-it-generator/tree/classtree/classtree_test.py b/17-it-generator/tree/classtree/classtree_test.py new file mode 100644 index 0000000..4535747 --- /dev/null +++ b/17-it-generator/tree/classtree/classtree_test.py @@ -0,0 +1,181 @@ +from textwrap import dedent +from typing import SupportsBytes +from classtree import tree, render_lines, main, subclasses + +from abc import ABCMeta + + +def test_subclasses(): + result = subclasses(UnicodeError) + assert set(result) >= {UnicodeEncodeError, UnicodeDecodeError} + + +def test_subclasses_of_type(): + """ + The `type` class is a special case because `type.__subclasses__()` + is an unbound method when called on it, so we must call it as + `type.__subclasses__(type)` just for `type`. + + This test does not verify the full list of results, but just + checks that `abc.ABCMeta` is included, because that's the only + subclass of `type` (i.e. metaclass) I we get when I run + `$ classtree.py type` at the command line. + + However, the Python console and `pytest` both load other modules, + so `subclasses` may find more subclasses of `type`—for example, + `enum.EnumMeta`. + """ + result = subclasses(type) + assert ABCMeta in result + + +def test_tree_1_level(): + result = list(tree(TabError)) + assert result == [(TabError, 0, True)] + + +def test_tree_2_levels(): + result = list(tree(IndentationError)) + assert result == [ + (IndentationError, 0, True), + (TabError, 1, True), + ] + + +def test_render_lines_1_level(): + result = list(render_lines(tree(TabError))) + assert result == ['TabError'] + + +def test_render_lines_2_levels_1_leaf(): + result = list(render_lines(tree(IndentationError))) + expected = [ + 'IndentationError', + '└── TabError', + ] + assert expected == result + + +def test_render_lines_3_levels_1_leaf(): + class X: pass + class Y(X): pass + class Z(Y): pass + result = list(render_lines(tree(X))) + expected = [ + 'X', + '└── Y', + ' └── Z', + ] + assert expected == result + + +def test_render_lines_4_levels_1_leaf(): + class Level0: pass + class Level1(Level0): pass + class Level2(Level1): pass + class Level3(Level2): pass + + result = list(render_lines(tree(Level0))) + expected = [ + 'Level0', + '└── Level1', + ' └── Level2', + ' └── Level3', + ] + assert expected == result + + +def test_render_lines_2_levels_2_leaves(): + class Branch: pass + class Leaf1(Branch): pass + class Leaf2(Branch): pass + result = list(render_lines(tree(Branch))) + expected = [ + 'Branch', + '├── Leaf1', + '└── Leaf2', + ] + assert expected == result + + +def test_render_lines_3_levels_2_leaves_dedent(): + class A: pass + class B(A): pass + class C(B): pass + class D(A): pass + class E(D): pass + + result = list(render_lines(tree(A))) + expected = [ + 'A', + '├── B', + '│ └── C', + '└── D', + ' └── E', + ] + assert expected == result + + +def test_render_lines_4_levels_4_leaves_dedent(): + class A: pass + class B1(A): pass + class C1(B1): pass + class D1(C1): pass + class D2(C1): pass + class C2(B1): pass + class B2(A): pass + expected = [ + 'A', + '├── B1', + '│ ├── C1', + '│ │ ├── D1', + '│ │ └── D2', + '│ └── C2', + '└── B2', + ] + + result = list(render_lines(tree(A))) + assert expected == result + + +def test_main_simple(capsys): + main('IndentationError') + expected = dedent(""" + IndentationError + └── TabError + """).lstrip() + captured = capsys.readouterr() + assert captured.out == expected + + +def test_main_dotted(capsys): + main('collections.abc.Sequence') + expected = dedent(""" + Sequence + ├── ByteString + ├── MutableSequence + │ └── UserList + """).lstrip() + captured = capsys.readouterr() + assert captured.out.startswith(expected) + + +def test_main_class_not_found(capsys): + main('NoSuchClass') + expected = "*** 'NoSuchClass' not found in 'builtins'.\n" + captured = capsys.readouterr() + assert captured.out == expected + + +def test_main_module_not_found(capsys): + main('nosuch.module') + expected = "*** Could not import 'nosuch'.\n" + captured = capsys.readouterr() + assert captured.out == expected + + +def test_main_not_a_class(capsys): + main('collections.abc') + expected = "*** 'abc' is not a class.\n" + captured = capsys.readouterr() + assert captured.out == expected diff --git a/17-it-generator/tree/extra/drawtree.py b/17-it-generator/tree/extra/drawtree.py index 522666a..b74cd33 100644 --- a/17-it-generator/tree/extra/drawtree.py +++ b/17-it-generator/tree/extra/drawtree.py @@ -1,10 +1,26 @@ from tree import tree -SP = '\N{SPACE}' -HLIN = '\N{BOX DRAWINGS LIGHT HORIZONTAL}' # ─ -ELBOW = f'\N{BOX DRAWINGS LIGHT UP AND RIGHT}{HLIN*2}{SP}' # └── -TEE = f'\N{BOX DRAWINGS LIGHT VERTICAL AND RIGHT}{HLIN*2}{SP}' # ├── -PIPE = f'\N{BOX DRAWINGS LIGHT VERTICAL}{SP*3}' # │ +SP = '\N{SPACE}' +HLIN = '\N{BOX DRAWINGS LIGHT HORIZONTAL}' * 2 + SP # ── +VLIN = '\N{BOX DRAWINGS LIGHT VERTICAL}' + SP * 3 # │ +TEE = '\N{BOX DRAWINGS LIGHT VERTICAL AND RIGHT}' + HLIN # ├── +ELBOW = '\N{BOX DRAWINGS LIGHT UP AND RIGHT}' + HLIN # └── + + +def subclasses(cls): + try: + return cls.__subclasses__() + except TypeError: # handle the `type` type + return cls.__subclasses__(cls) + + +def tree(cls, level=0, last_sibling=True): + yield cls, level, last_sibling + children = subclasses(cls) + if children: + last = children[-1] + for child in children: + yield from tree(child, level+1, child is last) def render_lines(tree_iter): @@ -13,8 +29,8 @@ def render_lines(tree_iter): prefix = '' for cls, level, last in tree_iter: - prefix = prefix[:4 * (level-1)] - prefix = prefix.replace(TEE, PIPE).replace(ELBOW, SP*4) + prefix = prefix[:4 * (level - 1)] + prefix = prefix.replace(TEE, VLIN).replace(ELBOW, SP * 4) prefix += ELBOW if last else TEE yield prefix + cls.__name__ From 80f7f84274a47579e59c29a4657691525152c9d5 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Fri, 12 Nov 2021 11:33:12 -0300 Subject: [PATCH 093/127] 2e reviewed manuscript --- 02-array-seq/lispy/py3.10/lis.py | 10 +- 11-pythonic-obj/vector2d_v3.py | 4 +- 11-pythonic-obj/vector2d_v3_prophash.py | 4 +- 11-pythonic-obj/vector2d_v3_slots.py | 4 +- 18-with-match/lispy/py3.10/examples_test.py | 6 +- 18-with-match/lispy/py3.10/lis.py | 2 +- 18-with-match/lispy/py3.9/lis.py | 2 +- 20-executors/getflags/flags2_asyncio.py | 4 +- .../getflags/flags2_asyncio_executor.py | 12 +- 20-executors/getflags/flags3_asyncio.py | 119 ++++++++++++++++++ 20-executors/getflags/flags_asyncio.py | 12 +- .../getflags/httpx-error-tree/tree.py | 1 + 20-executors/getflags/tree.py | 38 ------ 21-async/mojifinder/tcp_mojifinder.py | 24 ++-- 21-async/mojifinder/web_mojifinder.py | 13 +- 22-dyn-attr-prop/bulkfood/bulkfood_v2prop.py | 4 +- 22-dyn-attr-prop/oscon/explore0.py | 11 +- 22-dyn-attr-prop/oscon/explore1.py | 7 +- 22-dyn-attr-prop/oscon/explore2.py | 7 +- 22-dyn-attr-prop/oscon/schedule_v1.py | 3 +- 22-dyn-attr-prop/oscon/schedule_v2.py | 10 +- 22-dyn-attr-prop/oscon/schedule_v3.py | 12 +- 22-dyn-attr-prop/oscon/schedule_v4.py | 12 +- 22-dyn-attr-prop/oscon/schedule_v4_hasattr.py | 12 +- 22-dyn-attr-prop/oscon/schedule_v5.py | 10 +- 23-descriptor/bulkfood/model_v5.py | 4 +- 23-descriptor/descriptorkinds.py | 25 ++-- .../checked/metaclass/checkedlib.py | 4 +- 24-class-metaprog/factories.py | 5 +- 24-class-metaprog/metabunch/from3.6/bunch.py | 5 +- .../metabunch/from3.6/bunch_test.py | 2 +- 24-class-metaprog/timeslice.py | 91 ++++++++++++++ 32 files changed, 323 insertions(+), 156 deletions(-) create mode 100755 20-executors/getflags/flags3_asyncio.py delete mode 100644 20-executors/getflags/tree.py create mode 100644 24-class-metaprog/timeslice.py diff --git a/02-array-seq/lispy/py3.10/lis.py b/02-array-seq/lispy/py3.10/lis.py index 150fca7..88d9345 100755 --- a/02-array-seq/lispy/py3.10/lis.py +++ b/02-array-seq/lispy/py3.10/lis.py @@ -84,7 +84,7 @@ def standard_env() -> Environment: '-': op.sub, '*': op.mul, '/': op.truediv, - '//': op.floordiv, + 'quotient': op.floordiv, '>': op.gt, '<': op.lt, '>=': op.ge, @@ -149,7 +149,7 @@ def evaluate(exp: Expression, env: Environment) -> Any: # end::EVAL_MATCH_TOP[] case int(x) | float(x): return x - case Symbol(name): + case Symbol() as name: return env[name] # tag::EVAL_MATCH_MIDDLE[] case ['quote', x]: # <1> @@ -161,12 +161,12 @@ def evaluate(exp: Expression, env: Environment) -> Any: return evaluate(alternative, env) case ['lambda', [*parms], *body] if body: # <3> return Procedure(parms, body, env) - case ['define', Symbol(name), value_exp]: # <4> + case ['define', Symbol() as name, value_exp]: # <4> env[name] = evaluate(value_exp, env) # end::EVAL_MATCH_MIDDLE[] - case ['define', [Symbol(name), *parms], *body] if body: + case ['define', [Symbol() as name, *parms], *body] if body: env[name] = Procedure(parms, body, env) - case ['set!', Symbol(name), value_exp]: + case ['set!', Symbol() as name, value_exp]: env.change(name, evaluate(value_exp, env)) case [func_exp, *args] if func_exp not in KEYWORDS: proc = evaluate(func_exp, env) diff --git a/11-pythonic-obj/vector2d_v3.py b/11-pythonic-obj/vector2d_v3.py index 9ee716c..0edffb7 100644 --- a/11-pythonic-obj/vector2d_v3.py +++ b/11-pythonic-obj/vector2d_v3.py @@ -79,8 +79,6 @@ >>> v1 = Vector2d(3, 4) >>> v2 = Vector2d(3.1, 4.2) - >>> hash(v1), hash(v2) - (7, 384307168202284039) >>> len({v1, v2}) 2 @@ -124,7 +122,7 @@ def __eq__(self, other): return tuple(self) == tuple(other) def __hash__(self): - return hash(self.x) ^ hash(self.y) + return hash((self.x, self.y)) def __abs__(self): return math.hypot(self.x, self.y) diff --git a/11-pythonic-obj/vector2d_v3_prophash.py b/11-pythonic-obj/vector2d_v3_prophash.py index 6d7dceb..1652cd4 100644 --- a/11-pythonic-obj/vector2d_v3_prophash.py +++ b/11-pythonic-obj/vector2d_v3_prophash.py @@ -81,8 +81,6 @@ >>> v1 = Vector2d(3, 4) >>> v2 = Vector2d(3.1, 4.2) - >>> hash(v1), hash(v2) - (7, 384307168202284039) >>> len({v1, v2}) 2 @@ -131,7 +129,7 @@ def __eq__(self, other): # tag::VECTOR_V3_HASH[] def __hash__(self): - return hash(self.x) ^ hash(self.y) + return hash((self.x, self.y)) # end::VECTOR_V3_HASH[] def __abs__(self): diff --git a/11-pythonic-obj/vector2d_v3_slots.py b/11-pythonic-obj/vector2d_v3_slots.py index c48fb5b..89da973 100644 --- a/11-pythonic-obj/vector2d_v3_slots.py +++ b/11-pythonic-obj/vector2d_v3_slots.py @@ -78,8 +78,6 @@ >>> v1 = Vector2d(3, 4) >>> v2 = Vector2d(3.1, 4.2) - >>> hash(v1), hash(v2) - (7, 384307168202284039) >>> len({v1, v2}) 2 @@ -126,7 +124,7 @@ def __eq__(self, other): return tuple(self) == tuple(other) def __hash__(self): - return hash(self.x) ^ hash(self.y) + return hash((self.x, self.y)) def __abs__(self): return math.hypot(self.x, self.y) diff --git a/18-with-match/lispy/py3.10/examples_test.py b/18-with-match/lispy/py3.10/examples_test.py index d207df0..7025dad 100644 --- a/18-with-match/lispy/py3.10/examples_test.py +++ b/18-with-match/lispy/py3.10/examples_test.py @@ -27,11 +27,13 @@ >>> inner_env = {'a': 2} >>> outer_env = {'a': 0, 'b': 1} >>> env = Environment(inner_env, outer_env) ->>> env['a'] = 111 # <1> +>>> env['a'] # <1> +2 +>>> env['a'] = 111 # <2> >>> env['c'] = 222 >>> env Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 1}) ->>> env.change('b', 333) # <2> +>>> env.change('b', 333) # <3> >>> env Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 333}) diff --git a/18-with-match/lispy/py3.10/lis.py b/18-with-match/lispy/py3.10/lis.py index 04cc28d..f2d982c 100755 --- a/18-with-match/lispy/py3.10/lis.py +++ b/18-with-match/lispy/py3.10/lis.py @@ -65,7 +65,7 @@ def parse_atom(token: str) -> Atom: class Environment(ChainMap[Symbol, Any]): "A ChainMap that allows changing an item in-place." - def change(self, key: Symbol, value: object) -> None: + def change(self, key: Symbol, value: Any) -> None: "Find where key is defined and change the value there." for map in self.maps: if key in map: diff --git a/18-with-match/lispy/py3.9/lis.py b/18-with-match/lispy/py3.9/lis.py index e868a14..e467211 100644 --- a/18-with-match/lispy/py3.9/lis.py +++ b/18-with-match/lispy/py3.9/lis.py @@ -62,7 +62,7 @@ def parse_atom(token: str) -> Atom: class Environment(ChainMap[Symbol, Any]): "A ChainMap that allows changing an item in-place." - def change(self, key: Symbol, value: object) -> None: + def change(self, key: Symbol, value: Any) -> None: "Find where key is defined and change the value there." for map in self.maps: if key in map: diff --git a/20-executors/getflags/flags2_asyncio.py b/20-executors/getflags/flags2_asyncio.py index a922db5..4f1849a 100755 --- a/20-executors/getflags/flags2_asyncio.py +++ b/20-executors/getflags/flags2_asyncio.py @@ -37,7 +37,7 @@ async def download_one(client: httpx.AsyncClient, try: async with semaphore: # <3> image = await get_flag(client, base_url, cc) - except httpx.HTTPStatusError as exc: # <5> + except httpx.HTTPStatusError as exc: # <4> res = exc.response if res.status_code == HTTPStatus.NOT_FOUND: status = DownloadStatus.NOT_FOUND @@ -45,7 +45,7 @@ async def download_one(client: httpx.AsyncClient, else: raise else: - await asyncio.to_thread(save_flag, image, f'{cc}.gif') # <6> + await asyncio.to_thread(save_flag, image, f'{cc}.gif') # <5> status = DownloadStatus.OK msg = 'OK' if verbose and msg: diff --git a/20-executors/getflags/flags2_asyncio_executor.py b/20-executors/getflags/flags2_asyncio_executor.py index 752b60f..62516b3 100755 --- a/20-executors/getflags/flags2_asyncio_executor.py +++ b/20-executors/getflags/flags2_asyncio_executor.py @@ -22,23 +22,23 @@ MAX_CONCUR_REQ = 1000 -async def get_flag(session: httpx.AsyncClient, # <2> +async def get_flag(client: httpx.AsyncClient, # <2> base_url: str, cc: str) -> bytes: url = f'{base_url}/{cc}/{cc}.gif'.lower() - resp = await session.get(url, timeout=3.1, follow_redirects=True) # <3> + resp = await client.get(url, timeout=3.1, follow_redirects=True) # <3> resp.raise_for_status() return resp.content -async def download_one(session: httpx.AsyncClient, +async def download_one(client: httpx.AsyncClient, cc: str, base_url: str, semaphore: asyncio.Semaphore, verbose: bool) -> DownloadStatus: try: async with semaphore: - image = await get_flag(session, base_url, cc) + image = await get_flag(client, base_url, cc) except httpx.HTTPStatusError as exc: res = exc.response if res.status_code == HTTPStatus.NOT_FOUND: @@ -64,8 +64,8 @@ async def supervisor(cc_list: list[str], concur_req: int) -> Counter[DownloadStatus]: # <1> counter: Counter[DownloadStatus] = Counter() semaphore = asyncio.Semaphore(concur_req) # <2> - async with httpx.AsyncClient() as session: - to_do = [download_one(session, cc, base_url, semaphore, verbose) + async with httpx.AsyncClient() as client: + to_do = [download_one(client, cc, base_url, semaphore, verbose) for cc in sorted(cc_list)] # <3> to_do_iter = asyncio.as_completed(to_do) # <4> if not verbose: diff --git a/20-executors/getflags/flags3_asyncio.py b/20-executors/getflags/flags3_asyncio.py new file mode 100755 index 0000000..9554dd2 --- /dev/null +++ b/20-executors/getflags/flags3_asyncio.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 + +"""Download flags of countries (with error handling). + +asyncio async/await version + +""" +# tag::FLAGS2_ASYNCIO_TOP[] +import asyncio +from collections import Counter +from http import HTTPStatus +from pathlib import Path + +import httpx +import tqdm # type: ignore + +from flags2_common import main, DownloadStatus, save_flag + +# low concurrency default to avoid errors from remote site, +# such as 503 - Service Temporarily Unavailable +DEFAULT_CONCUR_REQ = 5 +MAX_CONCUR_REQ = 1000 + +async def get_flag(client: httpx.AsyncClient, # <1> + base_url: str, + cc: str) -> bytes: + url = f'{base_url}/{cc}/{cc}.gif'.lower() + resp = await client.get(url, timeout=3.1, follow_redirects=True) # <2> + resp.raise_for_status() + return resp.content + +# tag::FLAGS3_ASYNCIO_GET_COUNTRY[] +async def get_country(client: httpx.AsyncClient, + base_url: str, + cc: str) -> str: # <1> + url = f'{base_url}/{cc}/metadata.json'.lower() + resp = await client.get(url, timeout=3.1, follow_redirects=True) + resp.raise_for_status() + metadata = resp.json() # <2> + return metadata['country'] # <3> +# end::FLAGS3_ASYNCIO_GET_COUNTRY[] + +# tag::FLAGS3_ASYNCIO_DOWNLOAD_ONE[] +async def download_one(client: httpx.AsyncClient, + cc: str, + base_url: str, + semaphore: asyncio.Semaphore, + verbose: bool) -> DownloadStatus: + try: + async with semaphore: # <1> + image = await get_flag(client, base_url, cc) + async with semaphore: # <2> + country = await get_country(client, base_url, cc) + except httpx.HTTPStatusError as exc: + res = exc.response + if res.status_code == HTTPStatus.NOT_FOUND: + status = DownloadStatus.NOT_FOUND + msg = f'not found: {res.url}' + else: + raise + else: + filename = country.replace(' ', '_') # <3> + await asyncio.to_thread(save_flag, image, f'{filename}.gif') + status = DownloadStatus.OK + msg = 'OK' + if verbose and msg: + print(cc, msg) + return status +# end::FLAGS3_ASYNCIO_DOWNLOAD_ONE[] + +# tag::FLAGS2_ASYNCIO_START[] +async def supervisor(cc_list: list[str], + base_url: str, + verbose: bool, + concur_req: int) -> Counter[DownloadStatus]: # <1> + counter: Counter[DownloadStatus] = Counter() + semaphore = asyncio.Semaphore(concur_req) # <2> + async with httpx.AsyncClient() as client: + to_do = [download_one(client, cc, base_url, semaphore, verbose) + for cc in sorted(cc_list)] # <3> + to_do_iter = asyncio.as_completed(to_do) # <4> + if not verbose: + to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list)) # <5> + error: httpx.HTTPError | None = None # <6> + for coro in to_do_iter: # <7> + try: + status = await coro # <8> + except httpx.HTTPStatusError as exc: + error_msg = 'HTTP error {resp.status_code} - {resp.reason_phrase}' + error_msg = error_msg.format(resp=exc.response) + error = exc # <9> + except httpx.RequestError as exc: + error_msg = f'{exc} {type(exc)}'.strip() + error = exc # <10> + except KeyboardInterrupt: + break + + if error: + status = DownloadStatus.ERROR # <11> + if verbose: + url = str(error.request.url) # <12> + cc = Path(url).stem.upper() # <13> + print(f'{cc} error: {error_msg}') + counter[status] += 1 + + return counter + +def download_many(cc_list: list[str], + base_url: str, + verbose: bool, + concur_req: int) -> Counter[DownloadStatus]: + coro = supervisor(cc_list, base_url, verbose, concur_req) + counts = asyncio.run(coro) # <14> + + return counts + +if __name__ == '__main__': + main(download_many, DEFAULT_CONCUR_REQ, MAX_CONCUR_REQ) +# end::FLAGS2_ASYNCIO_START[] diff --git a/20-executors/getflags/flags_asyncio.py b/20-executors/getflags/flags_asyncio.py index ba28065..588d166 100755 --- a/20-executors/getflags/flags_asyncio.py +++ b/20-executors/getflags/flags_asyncio.py @@ -17,15 +17,15 @@ from flags import BASE_URL, save_flag, main # <2> -async def download_one(session: AsyncClient, cc: str): # <3> - image = await get_flag(session, cc) +async def download_one(client: AsyncClient, cc: str): # <3> + image = await get_flag(client, cc) save_flag(image, f'{cc}.gif') print(cc, end=' ', flush=True) return cc -async def get_flag(session: AsyncClient, cc: str) -> bytes: # <4> +async def get_flag(client: AsyncClient, cc: str) -> bytes: # <4> url = f'{BASE_URL}/{cc}/{cc}.gif'.lower() - resp = await session.get(url, timeout=6.1, + resp = await client.get(url, timeout=6.1, follow_redirects=True) # <5> return resp.read() # <6> # end::FLAGS_ASYNCIO_TOP[] @@ -35,8 +35,8 @@ def download_many(cc_list: list[str]) -> int: # <1> return asyncio.run(supervisor(cc_list)) # <2> async def supervisor(cc_list: list[str]) -> int: - async with AsyncClient() as session: # <3> - to_do = [download_one(session, cc) + async with AsyncClient() as client: # <3> + to_do = [download_one(client, cc) for cc in sorted(cc_list)] # <4> res = await asyncio.gather(*to_do) # <5> diff --git a/20-executors/getflags/httpx-error-tree/tree.py b/20-executors/getflags/httpx-error-tree/tree.py index eff7870..950bbfe 100755 --- a/20-executors/getflags/httpx-error-tree/tree.py +++ b/20-executors/getflags/httpx-error-tree/tree.py @@ -5,6 +5,7 @@ def tree(cls, level=0, last_sibling=True): yield cls, level, last_sibling + # get RuntimeError and exceptions defined in httpx subclasses = [sub for sub in cls.__subclasses__() if sub is RuntimeError or sub.__module__ == 'httpx'] diff --git a/20-executors/getflags/tree.py b/20-executors/getflags/tree.py deleted file mode 100644 index 4f65697..0000000 --- a/20-executors/getflags/tree.py +++ /dev/null @@ -1,38 +0,0 @@ -import httpx - -def tree(cls, level=0): - yield cls.__name__, level - for sub_cls in cls.__subclasses__(): - yield from tree(sub_cls, level+1) - - -def display(cls): - for cls_name, level in tree(cls): - indent = ' ' * 4 * level - print(f'{indent}{cls_name}') - - -def find_roots(module): - exceptions = [] - for name in dir(module): - obj = getattr(module, name) - if isinstance(obj, type) and issubclass(obj, BaseException): - exceptions.append(obj) - roots = [] - for exc in exceptions: - root = True - for other in exceptions: - if exc is not other and issubclass(exc, other): - root = False - break - if root: - roots.append(exc) - return roots - - -def main(): - for exc in find_roots(httpx): - display(exc) - -if __name__ == '__main__': - main() diff --git a/21-async/mojifinder/tcp_mojifinder.py b/21-async/mojifinder/tcp_mojifinder.py index 8ff4e50..4da7188 100755 --- a/21-async/mojifinder/tcp_mojifinder.py +++ b/21-async/mojifinder/tcp_mojifinder.py @@ -14,26 +14,28 @@ async def finder(index: InvertedIndex, # <2> reader: asyncio.StreamReader, - writer: asyncio.StreamWriter): + writer: asyncio.StreamWriter) -> None: client = writer.get_extra_info('peername') # <3> while True: # <4> writer.write(PROMPT) # can't await! # <5> await writer.drain() # must await! # <6> data = await reader.readline() # <7> + if not data: # <8> + break try: - query = data.decode().strip() # <8> - except UnicodeDecodeError: # <9> + query = data.decode().strip() # <9> + except UnicodeDecodeError: # <10> query = '\x00' - print(f' From {client}: {query!r}') # <10> + print(f' From {client}: {query!r}') # <11> if query: - if ord(query[:1]) < 32: # <11> + if ord(query[:1]) < 32: # <12> break - results = await search(query, index, writer) # <12> - print(f' To {client}: {results} results.') # <13> + results = await search(query, index, writer) # <13> + print(f' To {client}: {results} results.') # <14> - writer.close() # <14> - await writer.wait_closed() # <15> - print(f'Close {client}.') # <16> + writer.close() # <15> + await writer.wait_closed() # <16> + print(f'Close {client}.') # <17> # end::TCP_MOJIFINDER_TOP[] # tag::TCP_MOJIFINDER_SEARCH[] @@ -52,7 +54,7 @@ async def search(query: str, # <1> # end::TCP_MOJIFINDER_SEARCH[] # tag::TCP_MOJIFINDER_MAIN[] -async def supervisor(index: InvertedIndex, host: str, port: int): +async def supervisor(index: InvertedIndex, host: str, port: int) -> None: server = await asyncio.start_server( # <1> functools.partial(finder, index), # <2> host, port) # <3> diff --git a/21-async/mojifinder/web_mojifinder.py b/21-async/mojifinder/web_mojifinder.py index 7e0ff96..5f0cf5c 100644 --- a/21-async/mojifinder/web_mojifinder.py +++ b/21-async/mojifinder/web_mojifinder.py @@ -7,25 +7,26 @@ from charindex import InvertedIndex -app = FastAPI( # <1> +STATIC_PATH = Path(__file__).parent.absolute() / 'static' # <1> + +app = FastAPI( # <2> title='Mojifinder Web', description='Search for Unicode characters by name.', ) -class CharName(BaseModel): # <2> +class CharName(BaseModel): # <3> char: str name: str -def init(app): # <3> +def init(app): # <4> app.state.index = InvertedIndex() - static = Path(__file__).parent.absolute() / 'static' # <4> - app.state.form = (static / 'form.html').read_text() + app.state.form = (STATIC_PATH / 'form.html').read_text() init(app) # <5> @app.get('/search', response_model=list[CharName]) # <6> async def search(q: str): # <7> - chars = app.state.index.search(q) + chars = sorted(app.state.index.search(q)) return ({'char': c, 'name': name(c)} for c in chars) # <8> @app.get('/', response_class=HTMLResponse, include_in_schema=False) diff --git a/22-dyn-attr-prop/bulkfood/bulkfood_v2prop.py b/22-dyn-attr-prop/bulkfood/bulkfood_v2prop.py index e34a7c3..b2cffab 100644 --- a/22-dyn-attr-prop/bulkfood/bulkfood_v2prop.py +++ b/22-dyn-attr-prop/bulkfood/bulkfood_v2prop.py @@ -30,8 +30,8 @@ >>> nutmeg = LineItem('Moluccan nutmeg', 8, 13.95) >>> nutmeg.weight, nutmeg.price # <1> (8, 13.95) - >>> sorted(vars(nutmeg).items()) # <2> - [('description', 'Moluccan nutmeg'), ('price', 13.95), ('weight', 8)] + >>> nutmeg.__dict__ # <2> + {'description': 'Moluccan nutmeg', 'weight': 8, 'price': 13.95} # end::LINEITEM_V2_PROP_DEMO[] diff --git a/22-dyn-attr-prop/oscon/explore0.py b/22-dyn-attr-prop/oscon/explore0.py index 1ea7843..c881d16 100644 --- a/22-dyn-attr-prop/oscon/explore0.py +++ b/22-dyn-attr-prop/oscon/explore0.py @@ -54,12 +54,15 @@ def __getattr__(self, name): # <2> except AttributeError: return FrozenJSON.build(self.__data[name]) # <4> + def __dir__(self): # <5> + return self.__data.keys() + @classmethod - def build(cls, obj): # <5> - if isinstance(obj, abc.Mapping): # <6> + def build(cls, obj): # <6> + if isinstance(obj, abc.Mapping): # <7> return cls(obj) - elif isinstance(obj, abc.MutableSequence): # <7> + elif isinstance(obj, abc.MutableSequence): # <8> return [cls.build(item) for item in obj] - else: # <8> + else: # <9> return obj # end::EXPLORE0[] diff --git a/22-dyn-attr-prop/oscon/explore1.py b/22-dyn-attr-prop/oscon/explore1.py index ecb53b0..6942079 100644 --- a/22-dyn-attr-prop/oscon/explore1.py +++ b/22-dyn-attr-prop/oscon/explore1.py @@ -62,11 +62,14 @@ def __init__(self, mapping): # end::EXPLORE1[] def __getattr__(self, name): - if hasattr(self.__data, name): + try: return getattr(self.__data, name) - else: + except AttributeError: return FrozenJSON.build(self.__data[name]) + def __dir__(self): # <5> + return self.__data.keys() + @classmethod def build(cls, obj): if isinstance(obj, abc.Mapping): diff --git a/22-dyn-attr-prop/oscon/explore2.py b/22-dyn-attr-prop/oscon/explore2.py index 8cb9348..b50d49f 100644 --- a/22-dyn-attr-prop/oscon/explore2.py +++ b/22-dyn-attr-prop/oscon/explore2.py @@ -47,8 +47,11 @@ def __init__(self, mapping): self.__data[key] = value def __getattr__(self, name): - if hasattr(self.__data, name): + try: return getattr(self.__data, name) - else: + except AttributeError: return FrozenJSON(self.__data[name]) # <4> + + def __dir__(self): + return self.__data.keys() # end::EXPLORE2[] diff --git a/22-dyn-attr-prop/oscon/schedule_v1.py b/22-dyn-attr-prop/oscon/schedule_v1.py index 1be6c41..12aab71 100644 --- a/22-dyn-attr-prop/oscon/schedule_v1.py +++ b/22-dyn-attr-prop/oscon/schedule_v1.py @@ -23,8 +23,7 @@ def __init__(self, **kwargs): self.__dict__.update(kwargs) # <1> def __repr__(self): - cls_name = self.__class__.__name__ - return f'<{cls_name} serial={self.serial!r}>' # <2> + return f'<{self.__class__.__name__} serial={self.serial!r}>' # <2> def load(path=JSON_PATH): records = {} # <3> diff --git a/22-dyn-attr-prop/oscon/schedule_v2.py b/22-dyn-attr-prop/oscon/schedule_v2.py index 35827eb..5f264f7 100644 --- a/22-dyn-attr-prop/oscon/schedule_v2.py +++ b/22-dyn-attr-prop/oscon/schedule_v2.py @@ -29,8 +29,7 @@ def __init__(self, **kwargs): self.__dict__.update(kwargs) def __repr__(self): - cls_name = self.__class__.__name__ - return f'<{cls_name} serial={self.serial!r}>' + return f'<{self.__class__.__name__} serial={self.serial!r}>' @staticmethod # <3> def fetch(key): @@ -44,10 +43,9 @@ def fetch(key): class Event(Record): # <1> def __repr__(self): - if hasattr(self, 'name'): # <2> - cls_name = self.__class__.__name__ - return f'<{cls_name} {self.name!r}>' - else: + try: + return f'<{self.__class__.__name__} {self.name!r}>' # <2> + except AttributeError: return super().__repr__() @property diff --git a/22-dyn-attr-prop/oscon/schedule_v3.py b/22-dyn-attr-prop/oscon/schedule_v3.py index 786a412..7683b2d 100644 --- a/22-dyn-attr-prop/oscon/schedule_v3.py +++ b/22-dyn-attr-prop/oscon/schedule_v3.py @@ -33,8 +33,7 @@ def __init__(self, **kwargs): self.__dict__.update(kwargs) def __repr__(self): - cls_name = self.__class__.__name__ - return f'<{cls_name} serial={self.serial!r}>' + return f'<{self.__class__.__name__} serial={self.serial!r}>' @staticmethod def fetch(key): @@ -46,11 +45,10 @@ def fetch(key): class Event(Record): def __repr__(self): - if hasattr(self, 'name'): # <3> - cls_name = self.__class__.__name__ - return f'<{cls_name} {self.name!r}>' - else: - return super().__repr__() # <4> + try: + return f'<{self.__class__.__name__} {self.name!r}>' + except AttributeError: + return super().__repr__() @property def venue(self): diff --git a/22-dyn-attr-prop/oscon/schedule_v4.py b/22-dyn-attr-prop/oscon/schedule_v4.py index a609917..5b968e1 100644 --- a/22-dyn-attr-prop/oscon/schedule_v4.py +++ b/22-dyn-attr-prop/oscon/schedule_v4.py @@ -32,8 +32,7 @@ def __init__(self, **kwargs): self.__dict__.update(kwargs) def __repr__(self): - cls_name = self.__class__.__name__ - return f'<{cls_name} serial={self.serial!r}>' + return f'<{self.__class__.__name__} serial={self.serial!r}>' @staticmethod def fetch(key): @@ -52,11 +51,10 @@ def __init__(self, **kwargs): # end::SCHEDULE4_INIT[] def __repr__(self): - if hasattr(self, 'name'): - cls_name = self.__class__.__name__ - return f'<{cls_name} {self.name!r}>' - else: - return super().__repr__() # <4> + try: + return f'<{self.__class__.__name__} {self.name!r}>' + except AttributeError: + return super().__repr__() @property def venue(self): diff --git a/22-dyn-attr-prop/oscon/schedule_v4_hasattr.py b/22-dyn-attr-prop/oscon/schedule_v4_hasattr.py index ac5d5de..abc72bf 100644 --- a/22-dyn-attr-prop/oscon/schedule_v4_hasattr.py +++ b/22-dyn-attr-prop/oscon/schedule_v4_hasattr.py @@ -31,8 +31,7 @@ def __init__(self, **kwargs): self.__dict__.update(kwargs) def __repr__(self): - cls_name = self.__class__.__name__ - return f'<{cls_name} serial={self.serial!r}>' + return f'<{self.__class__.__name__} serial={self.serial!r}>' @staticmethod def fetch(key): @@ -44,11 +43,10 @@ def fetch(key): class Event(Record): def __repr__(self): - if hasattr(self, 'name'): - cls_name = self.__class__.__name__ - return f'<{cls_name} {self.name!r}>' - else: - return super().__repr__() # <4> + try: + return f'<{self.__class__.__name__} {self.name!r}>' + except AttributeError: + return super().__repr__() @property def venue(self): diff --git a/22-dyn-attr-prop/oscon/schedule_v5.py b/22-dyn-attr-prop/oscon/schedule_v5.py index 2517e57..7a9543b 100644 --- a/22-dyn-attr-prop/oscon/schedule_v5.py +++ b/22-dyn-attr-prop/oscon/schedule_v5.py @@ -36,8 +36,7 @@ def __init__(self, **kwargs): self.__dict__.update(kwargs) def __repr__(self): - cls_name = self.__class__.__name__ - return f'<{cls_name} serial={self.serial!r}>' + return f'<{self.__class__.__name__} serial={self.serial!r}>' @staticmethod def fetch(key): @@ -49,10 +48,9 @@ def fetch(key): class Event(Record): def __repr__(self): - if hasattr(self, 'name'): - cls_name = self.__class__.__name__ - return f'<{cls_name} {self.name!r}>' - else: + try: + return f'<{self.__class__.__name__} {self.name!r}>' + except AttributeError: return super().__repr__() # tag::SCHEDULE5_CACHED_PROPERTY[] diff --git a/23-descriptor/bulkfood/model_v5.py b/23-descriptor/bulkfood/model_v5.py index 018afcd..4c35698 100644 --- a/23-descriptor/bulkfood/model_v5.py +++ b/23-descriptor/bulkfood/model_v5.py @@ -30,7 +30,7 @@ class NonBlank(Validated): def validate(self, name, value): value = value.strip() - if len(value) == 0: + if not value: # <2> raise ValueError(f'{name} cannot be blank') - return value # <2> + return value # <3> # end::MODEL_V5_VALIDATED_SUB[] diff --git a/23-descriptor/descriptorkinds.py b/23-descriptor/descriptorkinds.py index 44f4018..13df709 100644 --- a/23-descriptor/descriptorkinds.py +++ b/23-descriptor/descriptorkinds.py @@ -5,21 +5,18 @@ >>> obj = Managed() # <1> >>> obj.over # <2> - -> Overriding.__get__(, , - ) + -> Overriding.__get__(, , ) >>> Managed.over # <3> -> Overriding.__get__(, None, ) >>> obj.over = 7 # <4> -> Overriding.__set__(, , 7) >>> obj.over # <5> - -> Overriding.__get__(, , - ) + -> Overriding.__get__(, , ) >>> obj.__dict__['over'] = 8 # <6> >>> vars(obj) # <7> {'over': 8} >>> obj.over # <8> - -> Overriding.__get__(, , - ) + -> Overriding.__get__(, , ) # end::DESCR_KINDS_DEMO1[] @@ -50,8 +47,7 @@ >>> obj = Managed() >>> obj.non_over # <1> - -> NonOverriding.__get__(, , - ) + -> NonOverriding.__get__(, , ) >>> obj.non_over = 7 # <2> >>> obj.non_over # <3> 7 @@ -59,8 +55,7 @@ -> NonOverriding.__get__(, None, ) >>> del obj.non_over # <5> >>> obj.non_over # <6> - -> NonOverriding.__get__(, , - ) + -> NonOverriding.__get__(, , ) # end::DESCR_KINDS_DEMO3[] @@ -88,7 +83,7 @@ >>> Managed.spam() Traceback (most recent call last): ... - TypeError: spam() missing 1 required positional argument: 'self' + TypeError: Managed.spam() missing 1 required positional argument: 'self' >>> Managed.spam(obj) -> Managed.spam() >>> Managed.spam.__get__(obj) # doctest: +ELLIPSIS @@ -156,15 +151,15 @@ def cls_name(obj_or_cls): def display(obj): cls = type(obj) if cls is type: - return ''.format(obj.__name__) + return f'' elif cls in [type(None), int]: return repr(obj) else: - return '<{} object>'.format(cls_name(obj)) + return f'<{cls_name(obj)} object>' def print_args(name, *args): pseudo_args = ', '.join(display(x) for x in args) - print('-> {}.__{}__({})'.format(cls_name(args[0]), name, pseudo_args)) + print(f'-> {cls_name(args[0])}.__{name}__({pseudo_args})') ### essential classes for this example ### @@ -199,6 +194,6 @@ class Managed: # <5> non_over = NonOverriding() def spam(self): # <6> - print('-> Managed.spam({})'.format(display(self))) + print(f'-> Managed.spam({display(self)})') # end::DESCR_KINDS[] diff --git a/24-class-metaprog/checked/metaclass/checkedlib.py b/24-class-metaprog/checked/metaclass/checkedlib.py index 8207dbe..768a0f6 100644 --- a/24-class-metaprog/checked/metaclass/checkedlib.py +++ b/24-class-metaprog/checked/metaclass/checkedlib.py @@ -78,7 +78,9 @@ def __init__(self, name: str, constructor: Callable) -> None: self.storage_name = '_' + name # <1> self.constructor = constructor - def __get__(self, instance, owner=None): # <2> + def __get__(self, instance, owner=None): + if instance is None: # <2> + return self return getattr(instance, self.storage_name) # <3> def __set__(self, instance: Any, value: Any) -> None: diff --git a/24-class-metaprog/factories.py b/24-class-metaprog/factories.py index 38ec763..cf61d89 100644 --- a/24-class-metaprog/factories.py +++ b/24-class-metaprog/factories.py @@ -49,9 +49,8 @@ def __iter__(self) -> Iterator[Any]: # <5> yield getattr(self, name) def __repr__(self): # <6> - values = ', '.join( - '{}={!r}'.format(*i) for i in zip(self.__slots__, self) - ) + values = ', '.join(f'{name}={value!r}' + for name, value in zip(self.__slots__, self)) cls_name = self.__class__.__name__ return f'{cls_name}({values})' diff --git a/24-class-metaprog/metabunch/from3.6/bunch.py b/24-class-metaprog/metabunch/from3.6/bunch.py index a8b3d5a..aa0a65e 100644 --- a/24-class-metaprog/metabunch/from3.6/bunch.py +++ b/24-class-metaprog/metabunch/from3.6/bunch.py @@ -28,7 +28,7 @@ >>> Point(x=1, y=2, z=3) Traceback (most recent call last): ... - AttributeError: 'Point' object has no attribute 'z' + AttributeError: No slots left for: 'z' >>> p = Point(x=21) >>> p.y = 42 >>> p @@ -51,7 +51,8 @@ def __init__(self, **kwargs): # <4> for name, default in defaults.items(): # <5> setattr(self, name, kwargs.pop(name, default)) if kwargs: # <6> - setattr(self, *kwargs.popitem()) + extra = ', '.join(kwargs) + raise AttributeError(f'No slots left for: {extra!r}') def __repr__(self): # <7> rep = ', '.join(f'{name}={value!r}' diff --git a/24-class-metaprog/metabunch/from3.6/bunch_test.py b/24-class-metaprog/metabunch/from3.6/bunch_test.py index 322487a..166e600 100644 --- a/24-class-metaprog/metabunch/from3.6/bunch_test.py +++ b/24-class-metaprog/metabunch/from3.6/bunch_test.py @@ -26,7 +26,7 @@ def test_init(): def test_init_wrong_argument(): with pytest.raises(AttributeError) as exc: p = Point(x=1.2, y=3.4, flavor='coffee') - assert "no attribute 'flavor'" in str(exc.value) + assert 'flavor' in str(exc.value) def test_slots(): diff --git a/24-class-metaprog/timeslice.py b/24-class-metaprog/timeslice.py new file mode 100644 index 0000000..5a52457 --- /dev/null +++ b/24-class-metaprog/timeslice.py @@ -0,0 +1,91 @@ + +""" +Could this be valid Python? + + if now >= T[4:20:PM]: chill() + + +>>> t = T[4:20] +>>> t +T[4:20] +>>> h, m, s = t +>>> h, m, s +(4, 20, 0) +>>> t[11:59:AM] +T[11:59:AM] +>>> start = t[9:O1:PM] +>>> start +T[9:O1:PM] +>>> start.h, start.m, start.s, start.pm +(9, 1, 0, True) +>>> now = T[7:O1:PM] +>>> T[4:OO:PM] +T[4:OO:PM] +>>> now > T[4:20:PM] +True +""" + +import functools + +AM = -2 +PM = -1 + +for n in range(10): + globals()[f'O{n}'] = n +OO = 0 + +@functools.total_ordering +class T(): + + def __init__(self, arg): + if isinstance(arg, slice): + h = arg.start or 0 + m = arg.stop or 0 + s = arg.step or 0 + else: + h, m, s = 0, 0, arg + if m in (AM, PM): + self.pm = m == PM + m = 0 + elif s in (AM, PM): + self.pm = s == PM + s = 0 + else: + self.pm = None + self.h, self.m, self.s = h, m, s + + def __class_getitem__(cls, arg): + return cls(arg) + + def __getitem__(self, arg): + return(type(self)(arg)) + + def __repr__(self): + h, m, s = self.h, self.m, self.s or None + if m == 0: + m = f'OO' + elif m < 10: + m = f'O{m}' + s = '' if s is None else s + if self.pm is None: + pm = '' + else: + pm = ':' + ('AM', 'PM')[self.pm] + return f'T[{h}:{m}{s}{pm}]' + + def __iter__(self): + yield from (self.h, self.m, self.s) + + def __eq__(self, other): + return tuple(self) == tuple(other) + + def __lt__(self, other): + return tuple(self) < tuple(other) + + def __add__(self, other): + """ + >>> T[11:O5:AM] + 15 # TODO: preserve pm field + T[11:20] + """ + if isinstance(other, int): + return self[self.h:self.m + other:self.pm] From 6709e755ee4b7edc6fc3b183c72d76a8cb7a83d5 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 15 Jan 2022 13:24:21 -0300 Subject: [PATCH 094/127] added links and README.md --- links/FPY.LI.htaccess | 982 ++++++++++++++++++++++++++++++++++++++++++ links/README.md | 37 ++ links/custom.htaccess | 109 +++++ 3 files changed, 1128 insertions(+) create mode 100644 links/FPY.LI.htaccess create mode 100644 links/README.md create mode 100644 links/custom.htaccess diff --git a/links/FPY.LI.htaccess b/links/FPY.LI.htaccess new file mode 100644 index 0000000..579d7e4 --- /dev/null +++ b/links/FPY.LI.htaccess @@ -0,0 +1,982 @@ +ErrorDocument 404 /404.html + +# main resources +RedirectTemp /code https://github.com/fluentpython/example-code-2e +RedirectTemp /home https://www.fluentpython.com/ + +# URLs mentioned at least three times +RedirectTemp /bisect https://www.fluentpython.com/extra/ordered-sequences-with-bisect/ +RedirectTemp /cardxvi https://www.python.org/dev/peps/pep-0484/#the-numeric-tower +RedirectTemp /collec https://docs.python.org/3/library/collections.html +RedirectTemp /dask https://dask.org/ +RedirectTemp /dtmodel https://docs.python.org/3/reference/datamodel.html +RedirectTemp /descr101 https://www.python.org/download/releases/2.2.3/descrintro/ +RedirectTemp /descrhow https://docs.python.org/3/howto/descriptor.html +RedirectTemp /doctest https://docs.python.org/3/library/doctest.html +RedirectTemp /effectpy https://effectivepython.com/ +RedirectTemp /fmtspec https://docs.python.org/3/library/string.html#formatspec +RedirectTemp /gunicorn https://gunicorn.org/ +RedirectTemp /hashint https://www.fluentpython.com/extra/internals-of-sets-and-dicts/ +RedirectTemp /hattingh https://www.oreilly.com/library/view/using-asyncio-in/9781492075325/ +RedirectTemp /httpx https://www.python-httpx.org/ +RedirectTemp /initvar https://docs.python.org/3/library/dataclasses.html#init-only-variables +RedirectTemp /mypy https://mypy.readthedocs.io/en/stable/ +RedirectTemp /norvigdp http://norvig.com/design-patterns/ +RedirectTemp /nsphere https://en.wikipedia.org/wiki/N-sphere +RedirectTemp /oldcoro https://www.fluentpython.com/extra/classic-coroutines/ +RedirectTemp /pandas https://pandas.pydata.org/ +RedirectTemp /pep218 https://www.python.org/dev/peps/pep-0218/ +RedirectTemp /pep227 https://www.python.org/dev/peps/pep-0227/ +RedirectTemp /pep255 https://www.python.org/dev/peps/pep-0255/ +RedirectTemp /pep342 https://www.python.org/dev/peps/pep-0342/ +RedirectTemp /pep343 https://www.python.org/dev/peps/pep-0343/ +RedirectTemp /pep357 https://www.python.org/dev/peps/pep-0357/ +RedirectTemp /pep362 https://www.python.org/dev/peps/pep-0362/ +RedirectTemp /pep371 https://www.python.org/dev/peps/pep-0371/ +RedirectTemp /pep380 https://www.python.org/dev/peps/pep-0380/ +RedirectTemp /pep393 https://www.python.org/dev/peps/pep-0393/ +RedirectTemp /pep412 https://www.python.org/dev/peps/pep-0412/ +RedirectTemp /pep442 https://www.python.org/dev/peps/pep-0442/ +RedirectTemp /pep443 https://www.python.org/dev/peps/pep-0443/ +RedirectTemp /pep448 https://www.python.org/dev/peps/pep-0448/ +RedirectTemp /pep455 https://www.python.org/dev/peps/pep-0455/ +RedirectTemp /pep456 https://www.python.org/dev/peps/pep-0456/ +RedirectTemp /pep461 https://www.python.org/dev/peps/pep-0461/ +RedirectTemp /pep465 https://www.python.org/dev/peps/pep-0465/ +RedirectTemp /pep467 https://www.python.org/dev/peps/pep-0467/ +RedirectTemp /pep482 https://www.python.org/dev/peps/pep-0482/ +RedirectTemp /pep483 https://www.python.org/dev/peps/pep-0483/ +RedirectTemp /pep484 https://www.python.org/dev/peps/pep-0484/ +RedirectTemp /pep487 https://www.python.org/dev/peps/pep-0487/ +RedirectTemp /pep492 https://www.python.org/dev/peps/pep-0492/ +RedirectTemp /pep519 https://www.python.org/dev/peps/pep-0519/ +RedirectTemp /pep525 https://www.python.org/dev/peps/pep-0525/ +RedirectTemp /pep526 https://www.python.org/dev/peps/pep-0526/ +RedirectTemp /pep528 https://www.python.org/dev/peps/pep-0528/ +RedirectTemp /pep529 https://www.python.org/dev/peps/pep-0529/ +RedirectTemp /pep530 https://www.python.org/dev/peps/pep-0530/ +RedirectTemp /pep544 https://www.python.org/dev/peps/pep-0544/ +RedirectTemp /pep554 https://www.python.org/dev/peps/pep-0554/ +RedirectTemp /pep557 https://www.python.org/dev/peps/pep-0557/ +RedirectTemp /pep560 https://www.python.org/dev/peps/pep-0560/ +RedirectTemp /pep561 https://www.python.org/dev/peps/pep-0561/ +RedirectTemp /pep563 https://www.python.org/dev/peps/pep-0563/ +RedirectTemp /pep570 https://www.python.org/dev/peps/pep-0570/ +RedirectTemp /pep572 https://www.python.org/dev/peps/pep-0572/ +RedirectTemp /pep584 https://www.python.org/dev/peps/pep-0584/ +RedirectTemp /pep585 https://www.python.org/dev/peps/pep-0585/ +RedirectTemp /pep586 https://www.python.org/dev/peps/pep-0586/ +RedirectTemp /pep589 https://www.python.org/dev/peps/pep-0589/ +RedirectTemp /pep591 https://www.python.org/dev/peps/pep-0591/ +RedirectTemp /pep593 https://www.python.org/dev/peps/pep-0593/ +RedirectTemp /pep604 https://www.python.org/dev/peps/pep-0604/ +RedirectTemp /pep612 https://www.python.org/dev/peps/pep-0612/ +RedirectTemp /pep613 https://www.python.org/dev/peps/pep-0613/ +RedirectTemp /pep616 https://www.python.org/dev/peps/pep-0616/ +RedirectTemp /pep617 https://www.python.org/dev/peps/pep-0617/ +RedirectTemp /pep618 https://www.python.org/dev/peps/pep-0618/ +RedirectTemp /pep634 https://www.python.org/dev/peps/pep-0634/ +RedirectTemp /pep635 https://www.python.org/dev/peps/pep-0635/ +RedirectTemp /pep636 https://www.python.org/dev/peps/pep-0636/ +RedirectTemp /pep638 https://www.python.org/dev/peps/pep-0638/ +RedirectTemp /pep645 https://www.python.org/dev/peps/pep-0645/ +RedirectTemp /pep646 https://www.python.org/dev/peps/pep-0646/ +RedirectTemp /pep647 https://www.python.org/dev/peps/pep-0647/ +RedirectTemp /pep649 https://www.python.org/dev/peps/pep-0649/ +RedirectTemp /pep654 https://www.python.org/dev/peps/pep-0654/ +RedirectTemp /pep655 https://www.python.org/dev/peps/pep-0655/ +RedirectTemp /pep661 https://www.python.org/dev/peps/pep-0661/ +RedirectTemp /pep3099 https://www.python.org/dev/peps/pep-3099/ +RedirectTemp /pep3102 https://www.python.org/dev/peps/pep-3102/ +RedirectTemp /pep3104 https://www.python.org/dev/peps/pep-3104/ +RedirectTemp /pep3106 https://www.python.org/dev/peps/pep-3106/ +RedirectTemp /pep3107 https://www.python.org/dev/peps/pep-3107/ +RedirectTemp /pep3115 https://www.python.org/dev/peps/pep-3115/ +RedirectTemp /pep3118 https://www.python.org/dev/peps/pep-3118/ +RedirectTemp /pep3119 https://www.python.org/dev/peps/pep-3119/ +RedirectTemp /pep3129 https://www.python.org/dev/peps/pep-3129/ +RedirectTemp /pep3132 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /pep3141 https://www.python.org/dev/peps/pep-3141/ +RedirectTemp /pep3148 https://www.python.org/dev/peps/pep-3148/ +RedirectTemp /pep3155 https://www.python.org/dev/peps/pep-3155/ +RedirectTemp /pep3333 https://www.python.org/dev/peps/pep-3333/ +RedirectTemp /pypydif https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /shed4051 https://github.com/python/typeshed/issues/4051 +RedirectTemp /slatkin https://effectivepython.com/ +RedirectTemp /specattr https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine +RedirectTemp /typing https://docs.python.org/3/library/typing.html +RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ + +# Remaining URLs by chapter + +############################################################ p +RedirectTemp /p-1 https://mail.python.org/pipermail/python-list/2002-December/134521.html +RedirectTemp /p-2 https://docs.python.org/3.10/tutorial/ +RedirectTemp /p-3 https://docs.python.org/3/tutorial/ +RedirectTemp /p-9 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ +RedirectTemp /p-10 https://www.oreilly.com/online-learning/try-now.html +RedirectTemp /p-13 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ +RedirectTemp /p-14 https://www.oreilly.com/ +RedirectTemp /p-15 https://www.facebook.com/OReilly/ +RedirectTemp /p-16 https://twitter.com/oreillymedia +RedirectTemp /p-17 https://www.youtube.com/oreillymedia +RedirectTemp /p-18 https://stackoverflow.com/users/95810/alex-martelli +RedirectTemp /p-19 https://pythonpro.com.br +RedirectTemp /p-20 https://groups.google.com/g/python-brasil +RedirectTemp /p-21 https://www.coffeelab.com.br/ +RedirectTemp /p-22 https://garoa.net.br/wiki/P%C3%A1gina_principal +############################################################ a +RedirectTemp /a-1 https://groups.google.com/forum/#!topic/python-tulip/Y4bhLNbKs74 +RedirectTemp /a-2 https://docs.python.org/3/library/asyncio-eventloop.html#executor +RedirectTemp /a-3 https://www.youtube.com/watch?v=x-kB2o8sd5c +RedirectTemp /a-4 https://www.youtube.com/watch?v=OSGv2VnC0go +RedirectTemp /a-5 https://mail.python.org/pipermail/python-ideas/2015-March/032557.html +RedirectTemp /a-6 https://pypi.org/project/pep8/ +RedirectTemp /a-7 https://pypi.org/project/flake8/ +RedirectTemp /a-8 https://pypi.org/project/pyflakes/ +RedirectTemp /a-9 https://pypi.org/project/mccabe/ +RedirectTemp /a-10 https://google.github.io/styleguide/pyguide.html +RedirectTemp /a-11 https://flask.palletsprojects.com/en/1.1.x/styleguide/ +RedirectTemp /a-12 https://docs.python-guide.org/ +RedirectTemp /a-13 https://david.goodger.org/projects/pycon/2007/idiomatic/handout.html +RedirectTemp /a-14 https://docs.mongodb.com/manual/about/#about-the-documentation-process +RedirectTemp /a-15 https://blog.startifact.com/posts/older/what-is-pythonic.html +RedirectTemp /a-16 https://mail.python.org/pipermail/tutor/2003-October/thread.html#25930 +RedirectTemp /a-17 https://mail.python.org/pipermail/python-list/2003-April/192027.html +RedirectTemp /a-19 https://www.python.org/doc/essays/ +############################################################ 01 +RedirectTemp /1-1 http://hugunin.net/story_of_jython.html +RedirectTemp /1-3 https://docs.python.org/2/library/string.html#format-string-syntax +RedirectTemp /1-4 https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr +RedirectTemp /1-5 https://docs.python.org/3/library/stdtypes.html#truth +RedirectTemp /1-6 https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists +RedirectTemp /1-8 https://www.python.org/doc/humor/#the-zen-of-python +RedirectTemp /1-10 https://stackoverflow.com/users/95810/alex-martelli +RedirectTemp /1-11 https://en.wikipedia.org/wiki/Object_model +RedirectTemp /1-13 https://www.dourish.com/goodies/jargon.html +RedirectTemp /1-14 https://zopeinterface.readthedocs.io/en/latest/ +RedirectTemp /1-15 https://plone.org/ +############################################################ 02 +RedirectTemp /2-4 https://github.com/fluentpython/example-code-2e/blob/master/02-array-seq/listcomp_speed.py +RedirectTemp /2-6 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /2-8 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations +RedirectTemp /2-9 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations +RedirectTemp /2-11 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching +RedirectTemp /2-12 https://docs.python.org/3.10/whatsnew/3.10.html +RedirectTemp /2-13 https://en.wikipedia.org/wiki/Switch_statement#Fallthrough +RedirectTemp /2-14 https://en.wikipedia.org/wiki/Dangling_else +RedirectTemp /2-15 https://github.com/gvanrossum/patma/blob/3ece6444ef70122876fd9f0099eb9490a2d630df/EXAMPLES.md#case-6-a-very-deep-iterable-and-type-match-with-extraction +RedirectTemp /2-16 https://github.com/fluentpython/lispy/blob/main/original/norvig/lis.py +RedirectTemp /2-17 https://norvig.com/lispy.html +RedirectTemp /2-18 https://numpy.org/doc/stable/user/quickstart.html#indexing-slicing-and-iterating +RedirectTemp /2-19 https://pythontutor.com/ +RedirectTemp /2-20 https://en.wikipedia.org/wiki/Fluent_interface +RedirectTemp /2-23 https://docs.python.org/3/library/bisect.html#bisect.insort +RedirectTemp /2-24 https://stackoverflow.com/questions/4845418/when-should-a-memoryview-be-used/ +RedirectTemp /2-26 https://www.fluentpython.com/extra/parsing-binary-struct/ +RedirectTemp /2-27 http://www.netlib.org +RedirectTemp /2-28 https://pandas.pydata.org/ +RedirectTemp /2-29 https://scikit-learn.org/stable/ +RedirectTemp /2-32 https://docs.python.org/3/howto/sorting.html +RedirectTemp /2-33 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /2-34 https://bugs.python.org/issue2292 +RedirectTemp /2-36 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching +RedirectTemp /2-37 https://docs.python.org/3.10/whatsnew/3.10.html +RedirectTemp /2-39 https://www.python.org/dev/peps/pep-0636/#appendix-a-quick-intro +RedirectTemp /2-41 https://eli.thegreenplace.net/2011/11/28/less-copies-in-python-with-the-buffer-protocol-and-memoryviews/ +RedirectTemp /2-42 https://jakevdp.github.io/PythonDataScienceHandbook/ +RedirectTemp /2-43 https://www.labri.fr/perso/nrougier/from-python-to-numpy/ +RedirectTemp /2-44 https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html +RedirectTemp /2-45 http://www.fonts101.com/fonts/view/Uncategorized/34398/Dijkstra +RedirectTemp /2-46 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types +RedirectTemp /2-47 https://en.wikipedia.org/wiki/Timsort +RedirectTemp /2-48 http://www.groklaw.net/pdf3/OraGoogle-1202.pdf +RedirectTemp /2-49 https://www.python.org/doc/humor/#id9 +############################################################ 03 +RedirectTemp /3-5 https://www.python.org/dev/peps/pep-0584/#motivation +RedirectTemp /3-7 https://docs.python.org/3.10/c-api/typeobj.html#Py_TPFLAGS_MAPPING +RedirectTemp /3-8 https://docs.python.org/3/glossary.html#term-hashable +RedirectTemp /3-9 https://docs.python.org/3/glossary.html#term-hashable +RedirectTemp /3-11 http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf +RedirectTemp /3-12 https://github.com/pingo-io/pingo-py +RedirectTemp /3-13 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/missing.py +RedirectTemp /3-14 https://docs.python.org/3/library/collections.html#collections.ChainMap +RedirectTemp /3-15 https://docs.python.org/3/library/collections.html#collections.Counter +RedirectTemp /3-16 https://docs.python.org/3/library/shelve.html +RedirectTemp /3-17 https://docs.python.org/3/library/dbm.html +RedirectTemp /3-18 https://docs.python.org/3/library/pickle.html +RedirectTemp /3-19 https://nedbatchelder.com/blog/202006/pickles_nine_flaws.html +RedirectTemp /3-20 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py#L813 +RedirectTemp /3-22 https://mail.python.org/pipermail/python-dev/2015-May/140003.html +RedirectTemp /3-23 https://bugs.python.org/issue18986 +RedirectTemp /3-24 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/transformdict.py +RedirectTemp /3-33 http://gandenberger.org/2018/03/10/ordered-dicts-vs-ordereddict/ +RedirectTemp /3-35 https://www.pypy.org/ +RedirectTemp /3-36 https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html +RedirectTemp /3-37 https://www.npopov.com/2014/12/22/PHPs-new-hashtable-implementation.html +RedirectTemp /3-38 https://www.youtube.com/watch?v=66P5FMkWoVU +RedirectTemp /3-39 https://pyvideo.org/video/276/the-mighty-dictionary-55/ +RedirectTemp /3-40 https://www.youtube.com/watch?v=p33CVV29OG8 +RedirectTemp /3-41 https://docs.python.org/3/whatsnew/3.6.html#new-dict-implementation +RedirectTemp /3-43 https://www.youtube.com/watch?v=tGAngdU_8D8 +RedirectTemp /3-44 https://speakerdeck.com/ramalho/python-set-practice-at-pycon +RedirectTemp /3-45 https://github.com/standupdev/uintset +RedirectTemp /3-46 http://www.json.org/fatfree.html +RedirectTemp /3-47 https://twitter.com/mitsuhiko/status/1229385843585974272 +############################################################ 04 +RedirectTemp /4-1 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 +RedirectTemp /4-2 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ +RedirectTemp /4-3 https://www.fluentpython.com/extra/parsing-binary-struct/ +RedirectTemp /4-5 https://www.fluentpython.com/extra/multi-character-emojis/ +RedirectTemp /4-8 https://w3techs.com/technologies/overview/character_encoding +RedirectTemp /4-9 https://docs.python.org/3/library/codecs.html#codecs.register_error +RedirectTemp /4-10 https://docs.python.org/3/library/stdtypes.html#str.isascii +RedirectTemp /4-11 https://pypi.org/project/chardet/ +RedirectTemp /4-12 https://docs.python.org/3/library/codecs.html#encodings-and-unicode +RedirectTemp /4-13 https://nedbatchelder.com/text/unipain/unipain.html +RedirectTemp /4-16 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONIOENCODING +RedirectTemp /4-17 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONLEGACYWINDOWSSTDIO +RedirectTemp /4-18 https://docs.python.org/3/library/locale.html#locale.getpreferredencoding +RedirectTemp /4-19 http://www.w3.org/TR/charmod-norm/ +RedirectTemp /4-20 https://docs.python.org/3/library/locale.html?highlight=strxfrm#locale.strxfrm +RedirectTemp /4-21 https://github.com/jtauber/pyuca +RedirectTemp /4-22 http://www.unicode.org/Public/UCA/6.3.0/allkeys.txt +RedirectTemp /4-23 https://pypi.org/project/PyICU/ +RedirectTemp /4-24 https://docs.python.org/3.10/library/stdtypes.html#str.isalpha +RedirectTemp /4-25 https://en.wikipedia.org/wiki/Unicode_character_property#General_Category +RedirectTemp /4-26 https://en.wikipedia.org/wiki/Unicode_character_property +RedirectTemp /4-27 https://github.com/microsoft/terminal +RedirectTemp /4-28 https://docs.python.org/3/library/unicodedata.html +RedirectTemp /4-29 https://docs.python.org/3/reference/lexical_analysis.html#string-literal-concatenation +RedirectTemp /4-30 https://docs.python.org/3/library/re.html +RedirectTemp /4-31 https://nedbatchelder.com/text/unipain.html +RedirectTemp /4-32 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 +RedirectTemp /4-33 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ +RedirectTemp /4-34 https://regebro.wordpress.com/2011/03/23/unconfusing-unicode-what-is-unicode/ +RedirectTemp /4-35 https://docs.python.org/3/howto/unicode.html +RedirectTemp /4-36 https://diveintopython3.net/strings.html +RedirectTemp /4-37 https://diveintopython3.net/ +RedirectTemp /4-38 https://finderiko.com/python-book +RedirectTemp /4-39 https://docs.python.org/3.0/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit +RedirectTemp /4-40 https://lucumr.pocoo.org/2013/7/2/the-updated-guide-to-unicode/ +RedirectTemp /4-41 http://python-notes.curiousefficiency.org/en/latest/python3/binary_protocols.html +RedirectTemp /4-42 http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html +RedirectTemp /4-43 https://docs.python.org/3/library/codecs.html#standard-encodings +RedirectTemp /4-44 https://www.informit.com/store/unicode-demystified-a-practical-programmers-guide-to-9780201700527 +RedirectTemp /4-45 https://unicodebook.readthedocs.io/index.html +RedirectTemp /4-46 https://www.w3.org/International/wiki/Case_folding +RedirectTemp /4-47 http://www.w3.org/TR/charmod-norm/ +RedirectTemp /4-48 http://unicode.org/reports/tr15/ +RedirectTemp /4-49 http://www.unicode.org/faq/normalization.html +RedirectTemp /4-50 http://www.unicode.org/ +RedirectTemp /4-51 http://www.macchiato.com/unicode/nfc-faq +RedirectTemp /4-52 https://emojipedia.org/ +RedirectTemp /4-53 https://blog.emojipedia.org/correcting-the-record-on-the-first-emoji-set/ +RedirectTemp /4-54 http://emojitracker.com/ +RedirectTemp /4-55 http://www.unicode.org/glossary/#plain_text +RedirectTemp /4-56 http://www.methods.co.nz/asciidoc/ +RedirectTemp /4-57 https://atlas.oreilly.com/ +############################################################ 05 +RedirectTemp /5-1 https://docs.python.org/3/library/typing.html#typing.TypedDict +RedirectTemp /5-4 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations +RedirectTemp /5-5 https://docs.python.org/3/library/typing.html#typing.get_type_hints +RedirectTemp /5-6 https://docs.python.org/3.8/library/collections.html#collections.somenamedtuple._asdict +RedirectTemp /5-8 https://www.jetbrains.com/pycharm/ +RedirectTemp /5-10 https://www.python.org/dev/peps/pep-0484/#acceptable-type-hints +RedirectTemp /5-11 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass +RedirectTemp /5-13 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass +RedirectTemp /5-14 https://docs.python.org/3/library/dataclasses.html +RedirectTemp /5-15 https://docs.python.org/3/library/dataclasses.html#inheritance +RedirectTemp /5-16 https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations +RedirectTemp /5-22 https://dublincore.org/specifications/dublin-core/ +RedirectTemp /5-23 https://en.wikipedia.org/wiki/Dublin_Core +RedirectTemp /5-24 https://martinfowler.com/bliki/CodeSmell.html +RedirectTemp /5-25 https://martinfowler.com/books/refactoring.html +RedirectTemp /5-26 https://www.python.org/dev/peps/pep-0634/#class-patterns +RedirectTemp /5-30 https://docs.python.org/3/library/dataclasses.html +RedirectTemp /5-32 https://www.python.org/dev/peps/pep-0557/#id47 +RedirectTemp /5-33 https://www.python.org/dev/peps/pep-0557/#id48 +RedirectTemp /5-34 https://www.python.org/dev/peps/pep-0557/#id33 +RedirectTemp /5-35 https://realpython.com +RedirectTemp /5-36 https://realpython.com/python-data-classes/ +RedirectTemp /5-37 https://www.youtube.com/watch?v=T-TwcmT6Rcw +RedirectTemp /5-38 https://www.attrs.org/en/stable/ +RedirectTemp /5-39 https://glyph.twistedmatrix.com/2016/08/attrs.html +RedirectTemp /5-40 https://www.attrs.org/en/stable/why.html +RedirectTemp /5-41 https://github.com/dabeaz/cluegen +RedirectTemp /5-42 https://refactoring.guru/ +RedirectTemp /5-43 https://refactoring.guru/smells/data-class +RedirectTemp /5-44 https://web.archive.org/web/20190204130328/http://catb.org/esr/jargon/html/G/Guido.html +RedirectTemp /5-45 https://web.archive.org/web/20190211161610/http://catb.org/esr/jargon/html/index.html +RedirectTemp /5-47 https://www.attrs.org/en/stable/ +############################################################ 06 +RedirectTemp /6-3 https://www.olin.edu/faculty/profile/lynn-andrea-stein/ +RedirectTemp /6-4 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types +RedirectTemp /6-5 https://pythontutor.com/ +RedirectTemp /6-6 https://docs.python.org/3/library/copy.html +RedirectTemp /6-7 https://en.wikipedia.org/wiki/Principle_of_least_astonishment +RedirectTemp /6-8 https://docs.python.org/3/reference/datamodel.html#object.%5C_%5C_del__ +RedirectTemp /6-9 https://emptysqua.re/blog/pypy-garbage-collection-and-a-deadlock/ +RedirectTemp /6-15 https://www.youtube.com/watch?v=HHFCFJSPWrI&feature=youtu.be +RedirectTemp /6-16 http://pymotw.com/3/copy/ +RedirectTemp /6-17 http://pymotw.com/3/weakref/ +RedirectTemp /6-18 https://docs.python.org/3/library/gc.html +RedirectTemp /6-20 https://devguide.python.org/garbage_collector/ +RedirectTemp /6-21 https://devguide.python.org/ +RedirectTemp /6-22 https://www.python.org/dev/peps/pep-0442/ +RedirectTemp /6-23 https://en.wikipedia.org/wiki/String_interning +RedirectTemp /6-24 https://en.wikipedia.org/wiki/Haddocks%27_Eyes +RedirectTemp /6-25 https://thp.io/2012/python-gc/python_gc_final_2012-01-22.pdf +############################################################ 07 +RedirectTemp /7-1 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html +RedirectTemp /7-3 https://www.fluentpython.com/extra/function-introspection/ +RedirectTemp /7-5 https://docs.python.org/3/library/functions.html#map +RedirectTemp /7-6 https://en.wikipedia.org/wiki/Functional_programming +RedirectTemp /7-7 https://docs.python.org/3/howto/functional.html +RedirectTemp /7-8 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy +RedirectTemp /7-9 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters +RedirectTemp /7-10 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters +RedirectTemp /7-12 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy +RedirectTemp /7-14 https://docs.python.org/3/howto/functional.html +RedirectTemp /7-15 https://stackoverflow.com/questions/3252228/python-why-is-functools-partial-necessary +RedirectTemp /7-16 https://speakerdeck.com/ramalho/beyond-paradigms-berlin-edition +RedirectTemp /7-17 https://www.youtube.com/watch?v=bF3a2VYXxa0 +RedirectTemp /7-18 http://cs.brown.edu/~sk/Publications/Papers/Published/sk-teach-pl-post-linnaean/ +RedirectTemp /7-19 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html +RedirectTemp /7-20 https://raw.githubusercontent.com/python/cpython/main/Misc/HISTORY +RedirectTemp /7-21 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html +############################################################ 08 +RedirectTemp /8-1 https://www.python.org/dev/peps/pep-0484/#non-goals +RedirectTemp /8-4 https://github.com/python/typing/issues/182 +RedirectTemp /8-5 https://github.com/python/mypy/issues/731 +RedirectTemp /8-6 https://github.com/google/pytype +RedirectTemp /8-7 https://github.com/Microsoft/pyright +RedirectTemp /8-8 https://pyre-check.org/ +RedirectTemp /8-10 https://mypy.readthedocs.io/en/stable/introduction.html +RedirectTemp /8-11 https://mypy.readthedocs.io/en/stable/config_file.html +RedirectTemp /8-12 https://pypi.org/project/flake8/ +RedirectTemp /8-13 https://pypi.org/project/blue/ +RedirectTemp /8-14 https://pypi.org/project/black/ +RedirectTemp /8-16 https://wefearchange.org/2020/11/steeringcouncil.rst.html +RedirectTemp /8-17 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes +RedirectTemp /8-18 https://en.wikipedia.org/wiki/Barbara_Liskov +RedirectTemp /8-19 https://en.wikipedia.org/wiki/Behavioral_subtyping +RedirectTemp /8-22 https://www.python.org/dev/peps/pep-0585/#implementation +RedirectTemp /8-25 https://docs.python.org/3/library/typing.html#module-contents +RedirectTemp /8-26 https://en.wikipedia.org/wiki/Geohash +RedirectTemp /8-27 https://en.wikipedia.org/wiki/Inverted_index +RedirectTemp /8-29 https://docs.python.org/3/library/typing.html#typing.List +RedirectTemp /8-30 https://docs.python.org/3/library/typing.html#typing.Dict +RedirectTemp /8-31 https://docs.python.org/3/library/typing.html#typing.Set +RedirectTemp /8-32 https://www.python.org/dev/peps/pep-0585/#implementation +RedirectTemp /8-34 https://docs.python.org/3/library/numbers.html +RedirectTemp /8-37 https://docs.python.org/3/library/typing.html#typing.List +RedirectTemp /8-38 https://github.com/python/typeshed +RedirectTemp /8-39 https://github.com/python/typeshed/blob/66cd36268a6a667714efaa27198a41d0d7f89477/stdlib/2and3/math.pyi#L45 +RedirectTemp /8-41 https://docs.python.org/3/library/statistics.html#statistics.mode +RedirectTemp /8-42 https://docs.python.org/3/library/statistics.html#statistics.mode +RedirectTemp /8-46 https://docs.python.org/3/library/typing.html#typing.Callable +RedirectTemp /8-47 https://pypi.org/project/blue/ +RedirectTemp /8-48 https://www.python.org/dev/peps/pep-0484/#id38 +RedirectTemp /8-49 https://docs.google.com/document/d/1aXs1tpwzPjW9MdsG5dI7clNFyYayFBkcXwRDo-qvbIk/preview +RedirectTemp /8-50 https://www.oreilly.com/library/view/the-best-software/9781590595008/ +RedirectTemp /8-51 https://www.youtube.com/watch?v=YFexUDjHO6w +RedirectTemp /8-52 https://www.youtube.com/watch?v=YFexUDjHO6w&t=13m40s +RedirectTemp /8-53 https://bernat.tech/posts/the-state-of-type-hints-in-python/ +RedirectTemp /8-54 https://realpython.com/python-type-checking/ +RedirectTemp /8-55 https://cjolowicz.github.io/posts/hypermodern-python-04-typing/ +RedirectTemp /8-56 https://mypy.readthedocs.io/en/stable/index.html +RedirectTemp /8-57 https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html +RedirectTemp /8-58 https://mypy.readthedocs.io/en/stable/common_issues.html +RedirectTemp /8-60 https://github.com/typeddjango/awesome-python-typing +RedirectTemp /8-61 https://docs.python.org/3/library/functions.html#max +RedirectTemp /8-62 https://en.wikipedia.org/wiki/Linguistic_relativity +RedirectTemp /8-63 https://pypistats.org/top +RedirectTemp /8-64 https://github.com/psf/requests/issues/3855 +RedirectTemp /8-65 https://lwn.net/Articles/643399/ +RedirectTemp /8-66 https://docs.python-requests.org/en/master/api/#requests.request +RedirectTemp /8-67 https://queue.acm.org/detail.cfm?id=1039523 +############################################################ 09 +RedirectTemp /9-1 https://docs.python.org/3/library/dis.html +RedirectTemp /9-2 https://en.wikipedia.org/wiki/Memoization +RedirectTemp /9-3 https://numpy.org/doc/stable/user/basics.types.html +RedirectTemp /9-5 https://docs.python.org/3/library/functools.html#functools.singledispatch +RedirectTemp /9-7 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/README.md +RedirectTemp /9-8 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/01-how-you-implemented-your-python-decorator-is-wrong.md +RedirectTemp /9-9 https://wrapt.readthedocs.io/en/latest/ +RedirectTemp /9-10 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch09.html +RedirectTemp /9-11 https://pypi.org/project/decorator/ +RedirectTemp /9-12 https://wiki.python.org/moin/PythonDecoratorLibrary +RedirectTemp /9-13 http://web.archive.org/web/20201109032203/http://effbot.org/zone/closure.htm +RedirectTemp /9-14 https://www.python.org/dev/peps/pep-3104/ +RedirectTemp /9-15 https://www.python.org/dev/peps/pep-0227/ +RedirectTemp /9-16 https://www.python.org/dev/peps/pep-0443/ +RedirectTemp /9-17 https://www.artima.com/weblogs/viewpost.jsp?thread=101605 +RedirectTemp /9-18 https://reg.readthedocs.io/en/latest/ +RedirectTemp /9-19 https://morepath.readthedocs.io/en/latest/ +RedirectTemp /9-20 https://www.gnu.org/software/emacs/manual/html_node/elisp/Dynamic-Binding.html +RedirectTemp /9-21 http://www.paulgraham.com/rootsoflisp.html +RedirectTemp /9-22 http://www-formal.stanford.edu/jmc/recursive/recursive.html +RedirectTemp /9-23 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this +############################################################ 10 +RedirectTemp /10-1 https://en.wikipedia.org/wiki/Software_design_pattern +RedirectTemp /10-2 https://en.wikipedia.org/wiki/Iterator_pattern +RedirectTemp /10-4 https://github.com/python/mypy/issues/9397 +RedirectTemp /10-5 http://www.norvig.com/design-patterns/index.htm +RedirectTemp /10-6 https://pyvideo.org/video/1110/python-design-patterns/ +RedirectTemp /10-7 http://www.aleax.it/gdd_pydp.pdf +RedirectTemp /10-9 https://perl.plover.com/yak/design/ +RedirectTemp /10-11 https://en.wikipedia.org/wiki/Turtles_all_the_way_down +############################################################ 11 +RedirectTemp /11-1 https://blog.startifact.com/posts/older/what-is-pythonic.html +RedirectTemp /11-2 https://julien.danjou.info/guide-python-static-class-abstract-methods/ +RedirectTemp /11-3 https://docs.python.org/3/library/string.html#formatspec +RedirectTemp /11-5 https://docs.python.org/3/reference/lexical_analysis.html#f-strings +RedirectTemp /11-6 https://docs.python.org/3/library/string.html#format-string-syntax +RedirectTemp /11-7 https://docs.python.org/3/library/string.html#formatspec +RedirectTemp /11-8 https://docs.python.org/3/reference/datamodel.html#object.__hash__ +RedirectTemp /11-9 https://web.archive.org/web/20161025185040/http://pythonpaste.org/StyleGuide.html +RedirectTemp /11-10 https://docs.python.org/3/tutorial/modules.html#more-on-modules +RedirectTemp /11-11 https://docs.python.org/3/library/gettext.html#gettext.NullTranslations +RedirectTemp /11-12 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/mem_test.py +RedirectTemp /11-16 https://docs.python.org/3/reference/datamodel.html#basic-customization +RedirectTemp /11-18 http://esug.org/data/HistoricalDocuments/TheSmalltalkReport/ST07/04wo.pdf +RedirectTemp /11-19 https://docs.oracle.com/javase/tutorial/essential/environment/security.html +RedirectTemp /11-21 https://docs.oracle.com/javase/tutorial/essential/environment/security.html +############################################################ 12 +RedirectTemp /12-1 https://en.wikipedia.org/wiki/Vector_space_model +RedirectTemp /12-2 https://pypi.org/project/gensim/ +RedirectTemp /12-6 https://docs.python.org/3/library/functions.html#enumerate +RedirectTemp /12-7 https://mathworld.wolfram.com/Hypersphere.html +RedirectTemp /12-12 https://en.wikipedia.org/wiki/Fold_(higher-order_function) +RedirectTemp /12-13 https://docs.python.org/2.5/whatsnew/pep-357.html +RedirectTemp /12-15 https://docs.python.org/3/reference/datamodel.html#special-method-names +RedirectTemp /12-16 https://en.wikipedia.org/wiki/KISS_principle +RedirectTemp /12-17 https://mail.python.org/pipermail/python-list/2000-July/046184.html +RedirectTemp /12-18 https://en.wikipedia.org/wiki/Duck_typing +RedirectTemp /12-19 https://mail.python.org/mailman/listinfo/python-list +RedirectTemp /12-20 https://mail.python.org/pipermail/python-list/2003-April/218568.html +############################################################ 13 +RedirectTemp /13-2 https://docs.python.org/3/c-api/index.html +RedirectTemp /13-3 https://docs.python.org/3/c-api/sequence.html +RedirectTemp /13-7 https://github.com/python/cpython/blob/31ceccb2c77854893f3a754aca04bedd74bedb10/Lib/_collections_abc.py#L870 +RedirectTemp /13-8 https://en.wikipedia.org/wiki/Monkey_patch +RedirectTemp /13-9 https://www.gevent.org/api/gevent.monkey.html +RedirectTemp /13-10 https://docs.python.org/3/library/random.html#random.shuffle +RedirectTemp /13-11 https://docs.python.org/3/reference/datamodel.html#emulating-container-types +RedirectTemp /13-12 https://docs.python.org/3/library/collections.html#collections.namedtuple +RedirectTemp /13-13 https://github.com/python/typeshed/blob/24afb531ffd07083d6a74be917342195062f7277/stdlib/collections/__init__.pyi +RedirectTemp /13-14 https://docs.python.org/3/glossary.html#term-abstract-base-class +RedirectTemp /13-15 https://en.wikipedia.org/wiki/Duck_typing#History +RedirectTemp /13-16 http://ptgmedia.pearsoncmg.com/images/020163371x/items/item33.html +RedirectTemp /13-17 https://docs.python.org/3/library/bisect.html#bisect.bisect +RedirectTemp /13-20 https://github.com/python/cpython/blob/main/Lib/_collections_abc.py +RedirectTemp /13-21 https://github.com/python/cpython/blob/main/Lib/abc.py +RedirectTemp /13-22 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes +RedirectTemp /13-23 https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable +RedirectTemp /13-24 https://docs.python.org/3/library/abc.html +RedirectTemp /13-25 https://docs.python.org/dev/library/abc.html#abc.abstractmethod +RedirectTemp /13-26 https://docs.python.org/dev/library/abc.html +RedirectTemp /13-27 https://docs.python.org/3/library/os.html#os.urandom +RedirectTemp /13-28 https://github.com/python/mypy/issues/2922 +RedirectTemp /13-29 https://docs.python.org/3/library/stdtypes.html#truth +RedirectTemp /13-30 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py +RedirectTemp /13-31 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L369 +RedirectTemp /13-32 https://bugs.python.org/issue31333 +RedirectTemp /13-33 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Modules/_abc.c#L605 +RedirectTemp /13-34 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L881 +RedirectTemp /13-37 https://docs.python.org/3/library/typing.html#protocols +RedirectTemp /13-40 https://martinfowler.com/bliki/RoleInterface.html +RedirectTemp /13-41 https://en.wikipedia.org/wiki/Interface_segregation_principle +RedirectTemp /13-42 https://github.com/python/typeshed/blob/master/CONTRIBUTING.md +RedirectTemp /13-43 https://gist.github.com/asukakenji/ac8a05644a2e98f1d5ea8c299541fce9 +RedirectTemp /13-44 https://www.python.org/dev/peps/pep-0544/#merging-and-extending-protocols +RedirectTemp /13-45 https://numpy.org/devdocs/user/basics.types.html +RedirectTemp /13-46 https://github.com/python/typeshed/blob/master/stdlib/statistics.pyi +RedirectTemp /13-47 https://bugs.python.org/issue41974 +RedirectTemp /13-49 https://glyph.twistedmatrix.com/2020/07/new-duck.html +RedirectTemp /13-50 https://glyph.twistedmatrix.com/2021/03/interfaces-and-protocols.html +RedirectTemp /13-51 https://plone.org/ +RedirectTemp /13-52 https://trypyramid.com/ +RedirectTemp /13-53 https://twistedmatrix.com/trac/ +RedirectTemp /13-54 https://www.artima.com/articles/contracts-in-python +RedirectTemp /13-55 https://martinfowler.com/bliki/DynamicTyping.html +RedirectTemp /13-56 https://martinfowler.com/bliki/RoleInterface.html +RedirectTemp /13-57 https://mypy.readthedocs.io/en/stable/protocols.html +RedirectTemp /13-58 https://pymotw.com/3/abc/index.html +RedirectTemp /13-59 https://www.python.org/dev/peps/pep-3119/ +RedirectTemp /13-60 https://www.python.org/dev/peps/pep-3141/ +RedirectTemp /13-61 https://docs.python.org/3/library/numbers.html +RedirectTemp /13-62 https://github.com/python/mypy/issues/3186 +RedirectTemp /13-63 https://github.com/python/mypy/issues/3186 +RedirectTemp /13-64 https://martinfowler.com/articles/lean-inception/ +RedirectTemp /13-65 https://martinfowler.com +RedirectTemp /13-68 https://www.jetbrains.com/pycharm/ +RedirectTemp /13-69 https://wingware.com/ +RedirectTemp /13-70 https://code.visualstudio.com/ +############################################################ 14 +RedirectTemp /14-1 http://worrydream.com/EarlyHistoryOfSmalltalk/ +RedirectTemp /14-2 https://docs.python.org/3/tutorial/classes.html +RedirectTemp /14-3 https://docs.python.org/3/library/collections.html#ordereddict-examples-and-recipes +RedirectTemp /14-4 https://discuss.python.org/t/is-it-time-to-deprecate-unbound-super-methods/1833 +RedirectTemp /14-6 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /14-7 https://docs.python.org/3/library/collections.html +RedirectTemp /14-9 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /14-10 https://en.wikipedia.org/wiki/Breadth-first_search +RedirectTemp /14-11 https://www.python.org/download/releases/2.3/mro/ +RedirectTemp /14-12 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/uppermixin.py +RedirectTemp /14-13 https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html +RedirectTemp /14-14 https://docs.python.org/3/library/collections.abc.html +RedirectTemp /14-15 https://github.com/python/cpython/blob/8ece98a7e418c3c68a4c61bc47a2d0931b59a889/Lib/collections/__init__.py#L1084 +RedirectTemp /14-16 https://docs.python.org/3/library/http.server.html +RedirectTemp /14-17 https://docs.python.org/3/library/socketserver.html#socketserver.ForkingMixIn +RedirectTemp /14-18 https://docs.python.org/3/library/os.html#os.fork +RedirectTemp /14-19 https://en.wikipedia.org/wiki/POSIX +RedirectTemp /14-20 http://ccbv.co.uk/ +RedirectTemp /14-21 https://github.com/django/django/tree/main/django/views/generic +RedirectTemp /14-22 https://en.wikipedia.org/wiki/Template_method_pattern +RedirectTemp /14-23 https://docs.python.org/3/library/tkinter.html +RedirectTemp /14-24 https://docs.python.org/3/library/tkinter.ttk.html +RedirectTemp /14-25 https://docs.oracle.com/javase/10/docs/api/java/awt/package-tree.html +RedirectTemp /14-26 https://docs.oracle.com/javase/10/docs/api/javax/swing/package-tree.html +RedirectTemp /14-27 https://squeak.org/ +RedirectTemp /14-28 https://github.com/python/cpython/blob/8ed183391241f0c73e7ba7f42b1d49fc02985f7b/Lib/tkinter/__init__.py#L2618 +RedirectTemp /14-29 https://docs.python.org/3/library/socketserver.html +RedirectTemp /14-30 https://docs.python.org/3/library/socketserver.html#socketserver.BaseServer +RedirectTemp /14-32 https://docs.python.org/3/library/typing.html#typing.final +RedirectTemp /14-33 https://docs.python.org/3/library/typing.html#typing.Final +RedirectTemp /14-35 https://docs.python.org/3/library/collections.abc.html +RedirectTemp /14-36 https://hynek.me/articles/python-subclassing-redux/ +RedirectTemp /14-38 https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ +RedirectTemp /14-39 https://fuhm.net/super-harmful/ +RedirectTemp /14-40 https://www.artima.com/weblogs/viewpost.jsp?thread=246488 +RedirectTemp /14-41 https://www.artima.com/weblogs/viewpost.jsp?thread=281127 +RedirectTemp /14-42 https://www.artima.com/weblogs/viewpost.jsp?thread=246341 +RedirectTemp /14-43 https://www.artima.com/weblogs/viewpost.jsp?thread=246483 +RedirectTemp /14-44 https://www.artima.com/weblogs/viewpost.jsp?thread=236275 +RedirectTemp /14-45 https://www.artima.com/weblogs/viewpost.jsp?thread=236278 +RedirectTemp /14-46 https://www.artima.com/weblogs/viewpost.jsp?thread=237121 +RedirectTemp /14-47 https://python-patterns.guide/gang-of-four/composition-over-inheritance/ +RedirectTemp /14-48 https://python-patterns.guide/ +RedirectTemp /14-49 https://www.youtube.com/watch?v=3MNVP9-hglc +RedirectTemp /14-50 http://worrydream.com/EarlyHistoryOfSmalltalk/ +RedirectTemp /14-51 https://en.wikipedia.org/wiki/Polymorphism_(computer_science) +############################################################ 15 +RedirectTemp /15-1 https://www.youtube.com/watch?v=csL8DLXGNlU&t=92m5s +RedirectTemp /15-4 https://twitter.com/gwidion/status/1265384692464967680 +RedirectTemp /15-5 https://pypi.org/project/pydantic/ +RedirectTemp /15-7 https://google.github.io/pytype/faq.html +RedirectTemp /15-8 https://google.github.io/pytype/faq.html +RedirectTemp /15-9 https://lxml.de/ +RedirectTemp /15-10 https://docs.python.org/3/library/xml.etree.elementtree.html +RedirectTemp /15-11 https://mypy.readthedocs.io/en/stable/common_issues.html +RedirectTemp /15-12 https://mypy.readthedocs.io/en/stable/common_issues.html#types-of-empty-collections +RedirectTemp /15-13 https://github.com/python/typing/issues/182 +RedirectTemp /15-14 https://pypi.org/project/pydantic/ +RedirectTemp /15-16 https://mypy.readthedocs.io/en/stable/type_narrowing.html#casts +RedirectTemp /15-17 https://www.python.org/dev/peps/pep-0484/#casts +RedirectTemp /15-18 https://github.com/python/typeshed/issues/5535 +RedirectTemp /15-19 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams +RedirectTemp /15-20 https://en.wikipedia.org/wiki/Code_smell +RedirectTemp /15-21 https://mypy.readthedocs.io/en/stable/error_codes.html#error-codes +RedirectTemp /15-22 https://github.com/fluentpython/example-code-2e/blob/master/15-more-types/clip_annot.py +RedirectTemp /15-24 https://docs.python.org/3/library/typing.html#introspection-helpers +RedirectTemp /15-25 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations +RedirectTemp /15-27 https://www.python.org/dev/peps/pep-0563/#abstract +RedirectTemp /15-29 https://docs.python.org/3.10/howto/annotations.html +RedirectTemp /15-32 https://docs.python.org/3/library/typing.html#user-defined-generic-types +RedirectTemp /15-33 https://docs.python.org/3.10/library/typing.html#typing.FrozenSet +RedirectTemp /15-34 https://docs.python.org/3.10/library/typing.html#typing.Generator +RedirectTemp /15-36 https://docs.python.org/3.10/library/typing.html#typing.AsyncGenerator +RedirectTemp /15-62 https://www.oreilly.com/library/view/robust-python/9781098100650/ +RedirectTemp /15-63 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance +RedirectTemp /15-64 https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generic-types +RedirectTemp /15-65 https://mypy.readthedocs.io/en/stable/common_issues.html#variance +RedirectTemp /15-67 https://www.artima.com/weblogs/viewpost.jsp?thread=85551 +RedirectTemp /15-68 https://dl.acm.org/action/cookieAbsent +RedirectTemp /15-69 http://bracha.org/pluggableTypesPosition.pdf +RedirectTemp /15-70 https://www.atomickotlin.com/atomickotlin/ +RedirectTemp /15-71 https://www.informit.com/store/effective-java-9780134685991 +RedirectTemp /15-72 https://www.manning.com/books/programming-with-types +RedirectTemp /15-73 https://www.oreilly.com/library/view/programming-typescript/9781492037644/ +RedirectTemp /15-74 https://www.informit.com/store/dart-programming-language-9780321927705 +RedirectTemp /15-75 https://www.yodaiken.com/2017/09/15/bad-ideas-in-type-theory/ +RedirectTemp /15-76 https://www.yodaiken.com/2017/11/30/types-considered-harmful-ii/ +RedirectTemp /15-77 https://web.archive.org/web/20071010002142/http://weblogs.java.net/blog/arnold/archive/2005/06/generics_consid_1.html +RedirectTemp /15-78 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance +############################################################ 16 +RedirectTemp /16-1 http://www.gotw.ca/publications/c_family_interview.htm +RedirectTemp /16-2 https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations +RedirectTemp /16-4 https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#boolean-indexing +RedirectTemp /16-5 https://docs.python.org/3/library/collections.html#collections.Counter +RedirectTemp /16-6 https://docs.python.org/3/reference/datamodel.html#emulating-container-types +RedirectTemp /16-8 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations +RedirectTemp /16-10 https://www.fluentpython.com/lingo/#fail-fast +RedirectTemp /16-11 https://neopythonic.blogspot.com/2019/03/why-operators-are-useful.html +RedirectTemp /16-12 https://treyhunner.com/2019/03/python-deep-comparisons-and-code-readability/ +RedirectTemp /16-14 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations +RedirectTemp /16-15 https://docs.python.org/3/library/pathlib.html +RedirectTemp /16-16 https://pypi.org/project/scapy/ +RedirectTemp /16-17 https://scapy.readthedocs.io/en/latest/usage.html#stacking-layers +RedirectTemp /16-18 https://docs.python.org/3/library/functools.html#functools.total_ordering +RedirectTemp /16-19 https://wiki.illinois.edu//wiki/download/attachments/273416327/ingalls.pdf +RedirectTemp /16-20 https://wiki.illinois.edu//wiki/download/attachments/273416327/double-dispatch.pdf +RedirectTemp /16-21 http://www.gotw.ca/publications/c_family_interview.htm +RedirectTemp /16-22 https://doc.rust-lang.org/std/ops/index.html +RedirectTemp /16-23 https://www.fluentpython.com/lingo/#lazy +############################################################ 17 +RedirectTemp /17-1 http://www.paulgraham.com/icad.html +RedirectTemp /17-5 https://en.wikipedia.org/wiki/Sentinel_value +RedirectTemp /17-6 https://docs.python.org/3.10/library/functions.html#iter +RedirectTemp /17-7 https://docs.python.org/3.10/library/functions.html#iter +RedirectTemp /17-8 https://github.com/python/cpython/blob/b1930bf75f276cd7ca08c4455298128d89adf7d1/Lib/_collections_abc.py#L271 +RedirectTemp /17-9 https://github.com/python/cpython/blob/main/Lib/types.py#L6 +RedirectTemp /17-10 https://en.wikipedia.org/wiki/CLU_(programming_language) +RedirectTemp /17-12 https://docs.python.org/3/glossary.html +RedirectTemp /17-13 https://docs.python.org/3/glossary.html#term-generator-iterator +RedirectTemp /17-14 https://docs.python.org/3/glossary.html#term-generator-expression +RedirectTemp /17-15 https://marc.info/?l=python-list&m=141826925106951&w=2 +RedirectTemp /17-17 https://docs.python.org/3/library/itertools.html +RedirectTemp /17-18 https://docs.python.org/3/library/exceptions.html#exception-hierarchy +RedirectTemp /17-19 https://en.wikipedia.org/wiki/Depth-first_search +RedirectTemp /17-20 https://docs.python.org/3.10/library/typing.html#typing.TypeAlias +RedirectTemp /17-22 https://docs.python.org/3/library/typing.html#typing.Generator +RedirectTemp /17-25 http://www.dabeaz.com/coroutines/Coroutines.pdf +RedirectTemp /17-26 http://www.dabeaz.com/coroutines/Coroutines.pdf +RedirectTemp /17-27 https://mail.python.org/pipermail/python-ideas/2009-April/003841.html +RedirectTemp /17-28 https://mail.python.org/pipermail/python-ideas/2009-April/003912.html +RedirectTemp /17-31 https://docs.python.org/3/library/exceptions.html#StopIteration +RedirectTemp /17-32 https://docs.python.org/3/reference/expressions.html#yield-expressions +RedirectTemp /17-33 https://docs.python.org/3/reference/index.html +RedirectTemp /17-36 http://catb.org/~esr/jargon/html/G/grok.html +RedirectTemp /17-37 https://docs.python.org/3/reference/expressions.html#yieldexpr +RedirectTemp /17-39 https://docs.python.org/3/library/itertools.html#itertools-recipes +RedirectTemp /17-40 https://more-itertools.readthedocs.io/en/stable/index.html +RedirectTemp /17-41 https://rittau.org/2006/11/java-iterators-are-not-iterable/ +RedirectTemp /17-42 https://docs.python.org/3/whatsnew/3.3.html#pep-380-syntax-for-delegating-to-a-subgenerator +RedirectTemp /17-45 http://www.dabeaz.com/generators/ +RedirectTemp /17-46 http://www.dabeaz.com/coroutines/ +RedirectTemp /17-47 https://archive.org/details/pyvideo_213___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-1-of-3 +RedirectTemp /17-48 https://archive.org/details/pyvideo_215___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-2-of-3 +RedirectTemp /17-49 https://archive.org/details/pyvideo_214___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-3-of-3 +RedirectTemp /17-50 http://www.dabeaz.com/finalgenerator/ +RedirectTemp /17-51 https://web.archive.org/web/20200218150637/http://seriously.dontusethiscode.com/2013/05/01/greedy-coroutine.html +RedirectTemp /17-52 https://effectivepython.com/ +RedirectTemp /17-53 https://effectivepython.com/2015/03/10/consider-coroutines-to-run-many-functions-concurrently +RedirectTemp /17-54 https://en.wikipedia.org/wiki/Conway's_Game_of_Life +RedirectTemp /17-55 https://gist.github.com/ramalho/da5590bc38c973408839 +RedirectTemp /17-56 https://gist.github.com/ramalho/da5590bc38c973408839 +RedirectTemp /17-57 https://journal.code4lib.org/articles/4893 +RedirectTemp /17-58 https://github.com/fluentpython/isis2json +RedirectTemp /17-59 https://github.com/fluentpython/isis2json/blob/master/README.rst +############################################################ 18 +RedirectTemp /18-1 https://pyvideo.org/video/1669/keynote-3/ +RedirectTemp /18-2 https://docs.python.org/3/library/sqlite3.html#using-the-connection-as-a-context-manager +RedirectTemp /18-3 https://docs.python.org/3/library/threading.html#using-locks-conditions-and-semaphores-in-the-with-statement +RedirectTemp /18-4 https://docs.python.org/3/library/decimal.html#decimal.localcontext +RedirectTemp /18-5 https://docs.python.org/3/library/unittest.mock.html#patch +RedirectTemp /18-6 https://docs.python.org/3/library/contextlib.html#contextlib.redirect_stdout +RedirectTemp /18-7 https://docs.python.org/3/library/sys.html#sys.exc_info +RedirectTemp /18-9 https://en.wikipedia.org/wiki/LL_parser +RedirectTemp /18-10 https://docs.python.org/3/library/contextlib.html +RedirectTemp /18-11 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ +RedirectTemp /18-12 https://docs.python.org/3/library/fileinput.html#fileinput.input +RedirectTemp /18-13 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ +RedirectTemp /18-14 https://en.wikipedia.org/wiki/Euclidean_algorithm +RedirectTemp /18-15 https://github.com/fluentpython/example-code-2e/tree/master/18-with-match/lispy/py3.10/ +RedirectTemp /18-17 https://github.com/python/typeshed/issues/6042 +RedirectTemp /18-18 https://github.com/fluentpython/lispy/tree/main/mylis +RedirectTemp /18-19 https://mitpress.mit.edu/sites/default/files/sicp/index.html +RedirectTemp /18-20 https://www.python.org/dev/peps/pep-0634/#or-patterns +RedirectTemp /18-21 https://en.wikipedia.org/wiki/Lambda#Character_encodings +RedirectTemp /18-22 https://docs.python.org/3/reference/compound_stmts.html +RedirectTemp /18-23 https://docs.python.org/3/glossary.html#term-eafp +RedirectTemp /18-24 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 +RedirectTemp /18-25 https://docs.python.org/3/reference/compound_stmts.html +RedirectTemp /18-26 https://stackoverflow.com/questions/16138232/is-it-a-good-practice-to-use-try-except-else-in-python +RedirectTemp /18-27 https://docs.python.org/3/library/stdtypes.html#typecontextmanager +RedirectTemp /18-28 https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers +RedirectTemp /18-30 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 +RedirectTemp /18-31 https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger-1?slide=34 +RedirectTemp /18-32 https://preshing.com/20110920/the-python-with-statement-by-example/ +RedirectTemp /18-33 https://www.rath.org/on-the-beauty-of-pythons-exitstack.html +RedirectTemp /18-34 https://github.com/norvig/pytudes +RedirectTemp /18-35 https://github.com/fluentpython/lispy +RedirectTemp /18-36 https://racket-lang.org/ +RedirectTemp /18-37 https://pyvideo.org/video/1669/keynote-3/ +RedirectTemp /18-38 https://en.wikipedia.org/wiki/Tail_call +RedirectTemp /18-39 https://2ality.com/2015/06/tail-call-optimization.html +RedirectTemp /18-40 http://neopythonic.blogspot.com/2009/04/final-words-on-tail-calls.html +RedirectTemp /18-41 https://webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit/ +RedirectTemp /18-42 http://kangax.github.io/compat-table/es6/ +RedirectTemp /18-43 https://world.hey.com/mgmarlow/what-happened-to-proper-tail-calls-in-javascript-5494c256 +RedirectTemp /18-44 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html +RedirectTemp /18-45 https://github.com/fluentpython/lispy/blob/main/mylis/mylis_2/lis.py +RedirectTemp /18-46 https://github.com/fluentpython/lispy/tree/main/mylis +############################################################ 19 +RedirectTemp /19-1 https://go.dev/blog/waza-talk +RedirectTemp /19-2 https://en.wikipedia.org/wiki/Graphics_processing_unit +RedirectTemp /19-3 https://docs.python.org/3/library/sys.html#sys.getswitchinterval +RedirectTemp /19-4 https://docs.python.org/3/library/sys.html#sys.setswitchinterval +RedirectTemp /19-5 https://en.wikipedia.org/wiki/System_call +RedirectTemp /19-6 https://mail.python.org/pipermail/python-dev/2009-October/093356.html +RedirectTemp /19-8 http://www.dabeaz.com/finalgenerator/ +RedirectTemp /19-9 https://docs.python.org/3/library/threading.html#thread-objects +RedirectTemp /19-10 https://www.pypy.org/ +RedirectTemp /19-11 https://mail.python.org/pipermail/python-list/2009-February/675659.html +RedirectTemp /19-12 https://en.wikipedia.org/wiki/Braille_Patterns +RedirectTemp /19-13 https://docs.python.org/3/library/multiprocessing.shared_memory.html +RedirectTemp /19-14 https://greenlet.readthedocs.io/en/latest/ +RedirectTemp /19-15 https://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html +RedirectTemp /19-16 http://www.gevent.org/ +RedirectTemp /19-17 https://github.com/gevent/gevent/wiki/Projects +RedirectTemp /19-19 https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor-example +RedirectTemp /19-20 https://github.com/python/asyncio/issues/284 +RedirectTemp /19-21 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor +RedirectTemp /19-23 https://docs.python.org/3/library/queue.html#queue.SimpleQueue.get +RedirectTemp /19-24 https://en.wikipedia.org/wiki/Race_condition +RedirectTemp /19-26 https://en.wikipedia.org/wiki/Context_switch +RedirectTemp /19-27 http://www.gotw.ca/publications/concurrency-ddj.htm +RedirectTemp /19-28 https://www.ansible.com/ +RedirectTemp /19-29 https://saltproject.io/ +RedirectTemp /19-30 https://www.fabfile.org/ +RedirectTemp /19-31 https://jupyter.org/ +RedirectTemp /19-32 https://docs.bokeh.org/en/latest/index.html +RedirectTemp /19-33 https://www.tensorflow.org/ +RedirectTemp /19-34 https://pytorch.org/ +RedirectTemp /19-35 https://www.oreilly.com/radar/where-programming-ops-ai-and-the-cloud-are-headed-in-2021/ +RedirectTemp /19-38 https://www.youtube.com/watch?v=ods97a5Pzw0 +RedirectTemp /19-39 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy +RedirectTemp /19-41 https://modwsgi.readthedocs.io/en/master/ +RedirectTemp /19-42 https://uwsgi-docs.readthedocs.io/en/latest/ +RedirectTemp /19-44 https://unit.nginx.org/ +RedirectTemp /19-45 https://www.techatbloomberg.com/blog/configuring-uwsgi-production-deployment/ +RedirectTemp /19-46 https://www.youtube.com/watch?v=p6R1h2Nn468 +RedirectTemp /19-47 https://asgi.readthedocs.io/en/latest/index.html +RedirectTemp /19-48 https://docs.celeryproject.org/en/stable/getting-started/introduction.html +RedirectTemp /19-49 https://python-rq.org/ +RedirectTemp /19-50 https://redis.io/ +RedirectTemp /19-51 https://realpython.com/intro-to-python-threading/ +RedirectTemp /19-52 https://pymotw.com/3/concurrency.html +RedirectTemp /19-54 https://docs.python.org/3/library/multiprocessing.html#programming-guidelines +RedirectTemp /19-56 https://docs.python.org/3/library/multiprocessing.html +RedirectTemp /19-57 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ +RedirectTemp /19-58 https://link.springer.com/book/10.1007/978-1-4842-5793-7?error=cookies_not_supported&code=2ed5d61d-ae9f-4f3d-94ac-0f68cf45ea4f +RedirectTemp /19-59 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 +RedirectTemp /19-61 https://greenteapress.com/wp/semaphores/ +RedirectTemp /19-62 https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock +RedirectTemp /19-63 https://www.artima.com/weblogs/viewpost.jsp?thread=214235 +RedirectTemp /19-64 http://jessenoller.com/blog/2009/02/01/python-threads-and-the-global-interpreter-lock +RedirectTemp /19-65 https://realpython.com/products/cpython-internals-book/ +RedirectTemp /19-66 http://www.dabeaz.com/GIL/ +RedirectTemp /19-67 http://www.dabeaz.com/python/UnderstandingGIL.pdf +RedirectTemp /19-68 https://bugs.python.org/issue7946#msg223110 +RedirectTemp /19-69 https://bugs.python.org/issue7946 +RedirectTemp /19-70 https://www.fullstackpython.com/ +RedirectTemp /19-71 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ +RedirectTemp /19-72 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 +RedirectTemp /19-73 https://www.packtpub.com/product/distributed-computing-with-python/9781785889691 +RedirectTemp /19-74 https://towardsdatascience.com/python-performance-and-gpus-1be860ffd58d?gi=6a57a172ab5e +RedirectTemp /19-75 https://www.oreilly.com/library/view/architecture-patterns-with/9781492052197/ +RedirectTemp /19-76 https://www.cosmicpython.com/ +RedirectTemp /19-77 https://pypi.org/project/lelo/ +RedirectTemp /19-78 https://github.com/npryce/python-parallelize +RedirectTemp /19-79 https://github.com/ericsnowcurrently/multi-core-python/wiki +RedirectTemp /19-81 https://gist.github.com/markshannon/79cace3656b40e21b7021504daee950c +RedirectTemp /19-82 https://en.wikipedia.org/wiki/Communicating_sequential_processes +RedirectTemp /19-83 https://github.com/stackless-dev/stackless/wiki +RedirectTemp /19-84 https://www.eveonline.com +RedirectTemp /19-85 https://www.ccpgames.com/ +RedirectTemp /19-86 https://stackless.readthedocs.io/en/3.6-slp/stackless-python.html#history +RedirectTemp /19-87 https://doc.pypy.org/en/latest/stackless.html +RedirectTemp /19-88 https://greenlet.readthedocs.io/en/latest/ +RedirectTemp /19-89 http://www.gevent.org/ +RedirectTemp /19-91 http://thespianpy.com/doc/ +RedirectTemp /19-92 https://pykka.readthedocs.io/en/latest/ +RedirectTemp /19-93 https://www.manning.com/books/rabbitmq-in-action +RedirectTemp /19-94 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ +RedirectTemp /19-95 https://en.wikipedia.org/wiki/OpenCL +RedirectTemp /19-96 https://media.pragprog.com/titles/pb7con/Bonus_Chapter.pdf +RedirectTemp /19-97 https://martinfowler.com/ +RedirectTemp /19-98 https://martinfowler.com/articles/patterns-of-distributed-systems/ +RedirectTemp /19-99 https://www.oreilly.com/library/view/oscon-2016-video/9781491965153/video247021.html +RedirectTemp /19-100 https://www.oreilly.com/library/view/designing-for-scalability/9781449361556/ +RedirectTemp /19-101 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy +RedirectTemp /19-102 https://en.wikipedia.org/wiki/KISS_principle +RedirectTemp /19-103 https://www.usenix.org/conference/hotos15/workshop-program/presentation/mcsherry +############################################################ 20 +RedirectTemp /20-1 https://www.artima.com/weblogs/viewpost.jsp?thread=299551 +RedirectTemp /20-3 https://docs.python.org/3/library/http.server.html +RedirectTemp /20-4 https://www.youtube.com/watch?v=A9e9Cy1UkME +RedirectTemp /20-5 https://www.cia.gov/the-world-factbook/ +RedirectTemp /20-7 https://docs.python-requests.org/en/latest/ +RedirectTemp /20-8 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed +RedirectTemp /20-9 https://docs.python.org/3/library/concurrent.futures.html +RedirectTemp /20-10 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.Executor +RedirectTemp /20-12 https://github.com/noamraph/tqdm +RedirectTemp /20-13 https://www.youtube.com/watch?v=M8Z65tAl5l4 +RedirectTemp /20-14 https://github.com/noamraph/tqdm/blob/master/README.md +RedirectTemp /20-15 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed +RedirectTemp /20-16 https://docs.python.org/3/library/asyncio-task.html#asyncio.as_completed +RedirectTemp /20-17 https://www.cloudflare.com/ +RedirectTemp /20-19 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags +RedirectTemp /20-21 https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418 +RedirectTemp /20-22 https://en.wikipedia.org/wiki/Embarrassingly_parallel +RedirectTemp /20-23 https://pyvideo.org/video/480/pyconau-2010--the-future-is-soon/ +RedirectTemp /20-25 http://www.dabeaz.com/coroutines/ +RedirectTemp /20-26 https://en.wikipedia.org/wiki/POSIX_Threads +RedirectTemp /20-27 https://en.wikipedia.org/wiki/C_dynamic_memory_allocation +RedirectTemp /20-28 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ +RedirectTemp /20-29 https://hexdocs.pm/ecto/getting-started.html +############################################################ 21 +RedirectTemp /21-1 https://docs.python.org/3/library/asyncio.html +RedirectTemp /21-6 https://bugs.python.org/issue43216 +RedirectTemp /21-7 https://bugs.python.org/issue36921 +RedirectTemp /21-8 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.getaddrinfo +RedirectTemp /21-9 https://docs.python.org/3/library/socket.html#socket.getaddrinfo +RedirectTemp /21-10 https://docs.python.org/3.10/library/asyncio-eventloop.html#asyncio.get_event_loop +RedirectTemp /21-11 https://www.python.org/dev/peps/pep-0492/#await-expression +RedirectTemp /21-14 https://www.fluentpython.com/extra/classic-coroutines/#yield_from_meaning_sec +RedirectTemp /21-17 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags +RedirectTemp /21-18 https://magicstack.github.io/asyncpg/current/ +RedirectTemp /21-19 https://magicstack.github.io/asyncpg/current/api/index.html#transactions +RedirectTemp /21-21 https://magicstack.github.io/asyncpg/current/api/index.html#transactions +RedirectTemp /21-22 https://magicstack.github.io/asyncpg/current/usage.html#connection-pools +RedirectTemp /21-23 https://gist.github.com/jboner/2841832 +RedirectTemp /21-24 https://en.wikipedia.org/wiki/Network-attached_storage +RedirectTemp /21-25 https://en.wikipedia.org/wiki/Semaphore_(programming) +RedirectTemp /21-26 https://en.wikipedia.org/wiki/Semaphore_(programming) +RedirectTemp /21-27 https://groups.google.com/forum/#!msg/python-tulip/PdAEtwpaJHs/7fqb-Qj2zJoJ +RedirectTemp /21-28 https://tritarget.org/#blog/2012/11/28/the-pyramid-of-doom-a-javascript-style-trap +RedirectTemp /21-29 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor +RedirectTemp /21-30 https://motor.readthedocs.io/en/stable/ +RedirectTemp /21-31 https://emptysqua.re/blog/response-to-asynchronous-python-and-databases/ +RedirectTemp /21-33 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams +RedirectTemp /21-35 https://en.wikipedia.org/wiki/Phaistos_Disc +RedirectTemp /21-36 https://en.wikipedia.org/wiki/Inverted_index +RedirectTemp /21-37 https://fastapi.tiangolo.com/ +RedirectTemp /21-38 https://swagger.io/specification/ +RedirectTemp /21-40 https://asgi.readthedocs.io/en/latest/implementations.html +RedirectTemp /21-41 https://pydantic-docs.helpmanual.io/ +RedirectTemp /21-42 https://doc.traefik.io/traefik/ +RedirectTemp /21-43 https://fastapi.tiangolo.com/project-generation/ +RedirectTemp /21-44 https://fastapi.tiangolo.com/tutorial/response-model/ +RedirectTemp /21-45 https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server +RedirectTemp /21-46 https://github.com/python/typeshed/issues/5535 +RedirectTemp /21-47 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Server.serve_forever +RedirectTemp /21-48 https://docs.python.org/3/library/asyncio-stream.html#asyncio.StreamWriter.close +RedirectTemp /21-49 https://docs.python.org/3/library/asyncio-stream.html#streamwriter +RedirectTemp /21-50 https://docs.python.org/3/library/asyncio-stream.html +RedirectTemp /21-51 https://docs.python.org/3/library/asyncio-protocol.html +RedirectTemp /21-52 https://docs.python.org/3/library/asyncio-protocol.html#tcp-echo-server +RedirectTemp /21-53 https://github.com/aio-libs/aiopg +RedirectTemp /21-54 https://docs.python.org/3/whatsnew/3.8.html#asyncio +RedirectTemp /21-56 https://datatracker.ietf.org/doc/html/rfc6761 +RedirectTemp /21-57 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager +RedirectTemp /21-59 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager +RedirectTemp /21-61 https://docs.python.org/3/library/asyncio-task.html#asyncio.gather +RedirectTemp /21-62 https://curio.readthedocs.io/en/latest/index.html +RedirectTemp /21-63 https://curio.readthedocs.io/en/latest/reference.html#task-groups +RedirectTemp /21-64 https://en.wikipedia.org/wiki/Structured_concurrency +RedirectTemp /21-66 https://www.python.org/dev/peps/pep-0654/#motivation +RedirectTemp /21-67 https://curio.readthedocs.io/en/latest/reference.html#AWAIT +RedirectTemp /21-68 https://www.python-httpx.org/async/#curio +RedirectTemp /21-69 https://github.com/dabeaz/curio/tree/78bca8a6ad677ef51e1568ac7b3e51441ab49c42/examples +RedirectTemp /21-70 https://datatracker.ietf.org/doc/html/rfc8305 +RedirectTemp /21-71 https://trio.readthedocs.io/en/stable/ +RedirectTemp /21-72 https://www.youtube.com/watch?v=M-sc73Y-zQA +RedirectTemp /21-73 https://en.wikipedia.org/wiki/Technical_debt +RedirectTemp /21-74 https://www.youtube.com/watch?v=E-1Y4kSsAFc +RedirectTemp /21-76 https://github.com/dabeaz/curio +RedirectTemp /21-77 https://trio.readthedocs.io/en/stable/ +RedirectTemp /21-78 https://curio.readthedocs.io/en/latest/#curio-university +RedirectTemp /21-79 https://docs.python.org/3/library/asyncio.html +RedirectTemp /21-80 https://bugs.python.org/issue33649 +RedirectTemp /21-82 https://docs.python.org/3/library/asyncio-dev.html +RedirectTemp /21-83 https://www.youtube.com/watch?v=iG6fr81xHKA +RedirectTemp /21-84 https://www.youtube.com/watch?v=F19R_M4Nay4 +RedirectTemp /21-85 https://asherman.io/projects/unsync.html +RedirectTemp /21-86 https://pyladies.com/ +RedirectTemp /21-87 https://www.youtube.com/watch?v=sW76-pRkZk8 +RedirectTemp /21-88 https://www.youtube.com/watch?v=Xbl7XjFYsN4 +RedirectTemp /21-89 https://www.youtube.com/watch?v=02CLD-42VdI +RedirectTemp /21-90 https://micropython.org/ +RedirectTemp /21-91 https://docs.micropython.org/en/latest/library/uasyncio.html +RedirectTemp /21-92 https://www.encode.io/articles/python-async-frameworks-beyond-developer-tribalism +RedirectTemp /21-93 https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ +RedirectTemp /21-94 https://github.com/MagicStack/uvloop +RedirectTemp /21-95 http://magic.io/blog/uvloop-blazing-fast-python-networking/ +RedirectTemp /21-96 https://github.com/MagicStack/httptools +RedirectTemp /21-97 https://docs.aiohttp.org/en/stable/ +RedirectTemp /21-98 https://github.com/wg/wrk +RedirectTemp /21-99 https://twistedmatrix.com/trac/ +############################################################ 22 +RedirectTemp /22-4 https://pypi.org/project/attrdict/ +RedirectTemp /22-5 https://pypi.org/project/addict/ +RedirectTemp /22-7 https://github.com/ActiveState/code/tree/master/recipes/Python/52308_simple_but_handy_collector_bunch_named_stuff +RedirectTemp /22-8 https://docs.python.org/3/library/types.html#types.SimpleNamespace +RedirectTemp /22-9 https://docs.python.org/3/library/argparse.html#argparse.Namespace +RedirectTemp /22-12 https://docs.python.org/3/library/functools.html#functools.cached_property +RedirectTemp /22-13 https://docs.python.org/3/library/functools.html#functools.cached_property +RedirectTemp /22-14 https://bugs.python.org/issue42781 +RedirectTemp /22-15 https://docs.python.org/3/howto/descriptor.html +RedirectTemp /22-16 https://docs.python.org/3/library/threading.html#rlock-objects +RedirectTemp /22-17 https://docs.python.org/3.10/library/functools.html#functools.cached_property +RedirectTemp /22-18 https://www.wsj.com/articles/SB10001424052970203914304576627102996831200 +RedirectTemp /22-19 https://www.youtube.com/watch?v=s35rVw1zskA&feature=youtu.be +RedirectTemp /22-20 https://docs.python.org/3/library/functions.html#dir +RedirectTemp /22-21 https://docs.python.org/3/library/functions.html#hasattr +RedirectTemp /22-22 https://docs.python.org/3.10/reference/datamodel.html#special-method-lookup +RedirectTemp /22-23 https://docs.python.org/3/library/functions.html +RedirectTemp /22-24 https://docs.python.org/3/reference/datamodel.html#customizing-attribute-access +RedirectTemp /22-25 https://docs.python.org/3/reference/datamodel.html#special-method-lookup +RedirectTemp /22-26 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /22-27 http://wiki.c2.com/?WelcomeVisitors +RedirectTemp /22-28 http://wiki.c2.com/?UniformAccessPrinciple +RedirectTemp /22-29 https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html +RedirectTemp /22-30 http://www.pingo.io/docs/ +RedirectTemp /22-31 https://www.drdobbs.com/javas-new-considered-harmful/184405016 +RedirectTemp /22-32 https://www.python.org/dev/peps/pep-0008/#class-names +############################################################ 23 +RedirectTemp /23-2 http://www.aleax.it/goo_pydp.pdf +RedirectTemp /23-3 https://docs.python.org/3.10/reference/datamodel.html#implementing-descriptors +RedirectTemp /23-6 https://docs.python.org/3/howto/descriptor.html +RedirectTemp /23-7 https://docs.python.org/3/howto/ +RedirectTemp /23-8 http://www.aleax.it/Python/nylug05_om.pdf +RedirectTemp /23-9 https://www.youtube.com/watch?v=VOzvpHoYQoo +RedirectTemp /23-11 https://www.python.org/dev/peps/pep-0487/#trait-descriptors +RedirectTemp /23-12 https://dreamsongs.com/RiseOfWorseIsBetter.html +RedirectTemp /23-13 http://web.archive.org/web/20031002184114/www.amk.ca/python/writing/warts.html +RedirectTemp /23-14 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this +RedirectTemp /23-15 http://python-history.blogspot.com/2009/02/adding-support-for-user-defined-classes.html +############################################################ 24 +RedirectTemp /24-2 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /24-3 https://docs.djangoproject.com/en/3.2/topics/db/models/#meta-options +RedirectTemp /24-4 https://www.python.org/dev/peps/pep-3155/ +RedirectTemp /24-7 https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions +RedirectTemp /24-8 https://go.dev/tour/basics/12 +RedirectTemp /24-9 https://bugs.python.org/issue42102 +RedirectTemp /24-11 https://www.python.org/dev/peps/pep-0557/#abstract +RedirectTemp /24-12 https://github.com/python/cpython/blob/3.9/Lib/dataclasses.py +RedirectTemp /24-13 https://docs.python.org/3/reference/datamodel.html#creating-the-class-object +RedirectTemp /24-15 https://mail.python.org/pipermail/python-list/2002-December/134521.html +RedirectTemp /24-16 https://www.oreilly.com/library/view/python-in-a/9781491913833/ +RedirectTemp /24-17 https://mail.python.org/pipermail/python-list/2002-July/162558.html +RedirectTemp /24-18 https://github.com/fluentpython/example-code/tree/master/21-class-metaprog/bulkfood +RedirectTemp /24-19 https://en.wikipedia.org/wiki/Principle_of_least_astonishment +RedirectTemp /24-21 https://en.wikipedia.org/wiki/Trait_(computer_programming) +RedirectTemp /24-22 https://en.wikipedia.org/wiki/Aspect-oriented_programming +RedirectTemp /24-23 https://dhh.dk/arc/000416.html +RedirectTemp /24-24 https://github.com/cjrh/autoslot +RedirectTemp /24-25 https://docs.python.org/3/reference/datamodel.html#customizing-class-creation +RedirectTemp /24-26 https://docs.python.org/3/library/functions.html#type +RedirectTemp /24-27 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /24-28 https://docs.python.org/3/library/types.html +RedirectTemp /24-29 https://www.python.org/dev/peps/pep-3129/ +RedirectTemp /24-30 https://www.youtube.com/watch?v=cAGliEJV9_o +RedirectTemp /24-31 https://docs.python.org/3/library/functools.html#functools.total_ordering +RedirectTemp /24-33 https://www.oreilly.com/library/view/python-in-a/9781491913833/ +RedirectTemp /24-36 https://www.python.org/download/releases/2.2.3/descrintro/ +RedirectTemp /24-42 https://github.com/lihaoyi/macropy +RedirectTemp /24-43 https://people.eecs.berkeley.edu/~bh/ss-toc2.html diff --git a/links/README.md b/links/README.md new file mode 100644 index 0000000..6ec844c --- /dev/null +++ b/links/README.md @@ -0,0 +1,37 @@ +# Short links for URLs in the book + +## The problem: link rot + +_Fluent Python, Second Edition_ has more than 1000 links to external resources. +Inevitably, some of those links will rot as time passes. +But I can't change the URLs in the print book! + +## The solution: indirection + +I replaced almost all URLs in the book with shortened versions that go through the `fpy.li` site which I control. +The site has an `.htaccess` file with *temporary* redirects. + +When I find out a link is stale, I can thange the redirect in `.htaccess` to a new target, +so the link in the book is back in service via the updated redirect. + +## Help wanted + +Please report broken links as bugs in the [`FPY.LI.htaccess`](FPY.LI.htaccess) file. +Also, feel free to send pull requests with fixes to that file. +When I accept a PR, I will redeploy it to `fpy.li/.htaccess`. + +## Details + +Almost all URLs in the book are replaced with shortened versions like +[`http://fpy.li/1-3`](http://fpy.li/1-3)—for chapter 1, link #3. + +There are also custom short URLs like +[`https://fpy.li/code`](https://fpy.li/code) which redirects to the example code repository. +I used custom short URLs for URLs with 3 or more mentions, or links to PEPs. + +Exceptions: + +- URLs with `oreilly` in them are unchanged; +- `fluentpython.com` URL (with no path) is unchanged; + +The `FPY.LI.htaccess` is deployed at the root folder in `http://fpy.li`. diff --git a/links/custom.htaccess b/links/custom.htaccess new file mode 100644 index 0000000..52649db --- /dev/null +++ b/links/custom.htaccess @@ -0,0 +1,109 @@ +ErrorDocument 404 /404.html + +# main resources +RedirectTemp /code https://github.com/fluentpython/example-code-2e +RedirectTemp /home https://www.fluentpython.com/ + +# URLs mentioned at least three times +RedirectTemp /bisect https://www.fluentpython.com/extra/ordered-sequences-with-bisect/ +RedirectTemp /cardxvi https://www.python.org/dev/peps/pep-0484/#the-numeric-tower +RedirectTemp /collec https://docs.python.org/3/library/collections.html +RedirectTemp /dask https://dask.org/ +RedirectTemp /dtmodel https://docs.python.org/3/reference/datamodel.html +RedirectTemp /descr101 https://www.python.org/download/releases/2.2.3/descrintro/ +RedirectTemp /descrhow https://docs.python.org/3/howto/descriptor.html +RedirectTemp /doctest https://docs.python.org/3/library/doctest.html +RedirectTemp /effectpy https://effectivepython.com/ +RedirectTemp /fmtspec https://docs.python.org/3/library/string.html#formatspec +RedirectTemp /gunicorn https://gunicorn.org/ +RedirectTemp /hashint https://www.fluentpython.com/extra/internals-of-sets-and-dicts/ +RedirectTemp /hattingh https://www.oreilly.com/library/view/using-asyncio-in/9781492075325/ +RedirectTemp /httpx https://www.python-httpx.org/ +RedirectTemp /initvar https://docs.python.org/3/library/dataclasses.html#init-only-variables +RedirectTemp /mypy https://mypy.readthedocs.io/en/stable/ +RedirectTemp /norvigdp http://norvig.com/design-patterns/ +RedirectTemp /nsphere https://en.wikipedia.org/wiki/N-sphere +RedirectTemp /oldcoro https://www.fluentpython.com/extra/classic-coroutines/ +RedirectTemp /pandas https://pandas.pydata.org/ +RedirectTemp /pep218 https://www.python.org/dev/peps/pep-0218/ +RedirectTemp /pep227 https://www.python.org/dev/peps/pep-0227/ +RedirectTemp /pep255 https://www.python.org/dev/peps/pep-0255/ +RedirectTemp /pep342 https://www.python.org/dev/peps/pep-0342/ +RedirectTemp /pep343 https://www.python.org/dev/peps/pep-0343/ +RedirectTemp /pep357 https://www.python.org/dev/peps/pep-0357/ +RedirectTemp /pep362 https://www.python.org/dev/peps/pep-0362/ +RedirectTemp /pep371 https://www.python.org/dev/peps/pep-0371/ +RedirectTemp /pep380 https://www.python.org/dev/peps/pep-0380/ +RedirectTemp /pep393 https://www.python.org/dev/peps/pep-0393/ +RedirectTemp /pep412 https://www.python.org/dev/peps/pep-0412/ +RedirectTemp /pep442 https://www.python.org/dev/peps/pep-0442/ +RedirectTemp /pep443 https://www.python.org/dev/peps/pep-0443/ +RedirectTemp /pep448 https://www.python.org/dev/peps/pep-0448/ +RedirectTemp /pep455 https://www.python.org/dev/peps/pep-0455/ +RedirectTemp /pep456 https://www.python.org/dev/peps/pep-0456/ +RedirectTemp /pep461 https://www.python.org/dev/peps/pep-0461/ +RedirectTemp /pep465 https://www.python.org/dev/peps/pep-0465/ +RedirectTemp /pep467 https://www.python.org/dev/peps/pep-0467/ +RedirectTemp /pep482 https://www.python.org/dev/peps/pep-0482/ +RedirectTemp /pep483 https://www.python.org/dev/peps/pep-0483/ +RedirectTemp /pep484 https://www.python.org/dev/peps/pep-0484/ +RedirectTemp /pep487 https://www.python.org/dev/peps/pep-0487/ +RedirectTemp /pep492 https://www.python.org/dev/peps/pep-0492/ +RedirectTemp /pep519 https://www.python.org/dev/peps/pep-0519/ +RedirectTemp /pep525 https://www.python.org/dev/peps/pep-0525/ +RedirectTemp /pep526 https://www.python.org/dev/peps/pep-0526/ +RedirectTemp /pep528 https://www.python.org/dev/peps/pep-0528/ +RedirectTemp /pep529 https://www.python.org/dev/peps/pep-0529/ +RedirectTemp /pep530 https://www.python.org/dev/peps/pep-0530/ +RedirectTemp /pep544 https://www.python.org/dev/peps/pep-0544/ +RedirectTemp /pep554 https://www.python.org/dev/peps/pep-0554/ +RedirectTemp /pep557 https://www.python.org/dev/peps/pep-0557/ +RedirectTemp /pep560 https://www.python.org/dev/peps/pep-0560/ +RedirectTemp /pep561 https://www.python.org/dev/peps/pep-0561/ +RedirectTemp /pep563 https://www.python.org/dev/peps/pep-0563/ +RedirectTemp /pep570 https://www.python.org/dev/peps/pep-0570/ +RedirectTemp /pep572 https://www.python.org/dev/peps/pep-0572/ +RedirectTemp /pep584 https://www.python.org/dev/peps/pep-0584/ +RedirectTemp /pep585 https://www.python.org/dev/peps/pep-0585/ +RedirectTemp /pep586 https://www.python.org/dev/peps/pep-0586/ +RedirectTemp /pep589 https://www.python.org/dev/peps/pep-0589/ +RedirectTemp /pep591 https://www.python.org/dev/peps/pep-0591/ +RedirectTemp /pep593 https://www.python.org/dev/peps/pep-0593/ +RedirectTemp /pep604 https://www.python.org/dev/peps/pep-0604/ +RedirectTemp /pep612 https://www.python.org/dev/peps/pep-0612/ +RedirectTemp /pep613 https://www.python.org/dev/peps/pep-0613/ +RedirectTemp /pep616 https://www.python.org/dev/peps/pep-0616/ +RedirectTemp /pep617 https://www.python.org/dev/peps/pep-0617/ +RedirectTemp /pep618 https://www.python.org/dev/peps/pep-0618/ +RedirectTemp /pep634 https://www.python.org/dev/peps/pep-0634/ +RedirectTemp /pep635 https://www.python.org/dev/peps/pep-0635/ +RedirectTemp /pep636 https://www.python.org/dev/peps/pep-0636/ +RedirectTemp /pep638 https://www.python.org/dev/peps/pep-0638/ +RedirectTemp /pep645 https://www.python.org/dev/peps/pep-0645/ +RedirectTemp /pep646 https://www.python.org/dev/peps/pep-0646/ +RedirectTemp /pep647 https://www.python.org/dev/peps/pep-0647/ +RedirectTemp /pep649 https://www.python.org/dev/peps/pep-0649/ +RedirectTemp /pep654 https://www.python.org/dev/peps/pep-0654/ +RedirectTemp /pep655 https://www.python.org/dev/peps/pep-0655/ +RedirectTemp /pep661 https://www.python.org/dev/peps/pep-0661/ +RedirectTemp /pep3099 https://www.python.org/dev/peps/pep-3099/ +RedirectTemp /pep3102 https://www.python.org/dev/peps/pep-3102/ +RedirectTemp /pep3104 https://www.python.org/dev/peps/pep-3104/ +RedirectTemp /pep3106 https://www.python.org/dev/peps/pep-3106/ +RedirectTemp /pep3107 https://www.python.org/dev/peps/pep-3107/ +RedirectTemp /pep3115 https://www.python.org/dev/peps/pep-3115/ +RedirectTemp /pep3118 https://www.python.org/dev/peps/pep-3118/ +RedirectTemp /pep3119 https://www.python.org/dev/peps/pep-3119/ +RedirectTemp /pep3129 https://www.python.org/dev/peps/pep-3129/ +RedirectTemp /pep3132 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /pep3141 https://www.python.org/dev/peps/pep-3141/ +RedirectTemp /pep3148 https://www.python.org/dev/peps/pep-3148/ +RedirectTemp /pep3155 https://www.python.org/dev/peps/pep-3155/ +RedirectTemp /pep3333 https://www.python.org/dev/peps/pep-3333/ +RedirectTemp /pypydif https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /shed4051 https://github.com/python/typeshed/issues/4051 +RedirectTemp /slatkin https://effectivepython.com/ +RedirectTemp /specattr https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine +RedirectTemp /typing https://docs.python.org/3/library/typing.html +RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ From 82b4fe2163174bfee23527e68d418eed2ae4f46b Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 15 Jan 2022 14:04:45 -0300 Subject: [PATCH 095/127] added links and README.md --- links/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/links/README.md b/links/README.md index 6ec844c..09c808a 100644 --- a/links/README.md +++ b/links/README.md @@ -1,18 +1,18 @@ # Short links for URLs in the book -## The problem: link rot +## Problem: link rot _Fluent Python, Second Edition_ has more than 1000 links to external resources. Inevitably, some of those links will rot as time passes. -But I can't change the URLs in the print book! +But I can't change the URLs in the print book... -## The solution: indirection +## Solution: indirection I replaced almost all URLs in the book with shortened versions that go through the `fpy.li` site which I control. The site has an `.htaccess` file with *temporary* redirects. When I find out a link is stale, I can thange the redirect in `.htaccess` to a new target, -so the link in the book is back in service via the updated redirect. +so the link in the book is back in service through the updated redirect. ## Help wanted From 646427f393a8d3463d8dc95f8eb0bc1744d2d002 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sun, 16 Jan 2022 19:05:00 -0300 Subject: [PATCH 096/127] updated .htaccess --- links/FPY.LI.htaccess | 1567 ++++++++++++++++++++++------------------- links/custom.htaccess | 18 +- 2 files changed, 838 insertions(+), 747 deletions(-) diff --git a/links/FPY.LI.htaccess b/links/FPY.LI.htaccess index 579d7e4..9f960f1 100644 --- a/links/FPY.LI.htaccess +++ b/links/FPY.LI.htaccess @@ -25,6 +25,17 @@ RedirectTemp /norvigdp http://norvig.com/design-patterns/ RedirectTemp /nsphere https://en.wikipedia.org/wiki/N-sphere RedirectTemp /oldcoro https://www.fluentpython.com/extra/classic-coroutines/ RedirectTemp /pandas https://pandas.pydata.org/ +RedirectTemp /pycook3 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ +RedirectTemp /pynut3 https://www.oreilly.com/library/view/python-in-a/9781491913833/ +RedirectTemp /pypydif https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /shed4051 https://github.com/python/typeshed/issues/4051 +RedirectTemp /slatkin https://effectivepython.com/ +RedirectTemp /specattr https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine +RedirectTemp /typing https://docs.python.org/3/library/typing.html +RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ + +# Python Enhancement Proposals RedirectTemp /pep218 https://www.python.org/dev/peps/pep-0218/ RedirectTemp /pep227 https://www.python.org/dev/peps/pep-0227/ RedirectTemp /pep255 https://www.python.org/dev/peps/pep-0255/ @@ -100,13 +111,6 @@ RedirectTemp /pep3141 https://www.python.org/dev/peps/pep-3141/ RedirectTemp /pep3148 https://www.python.org/dev/peps/pep-3148/ RedirectTemp /pep3155 https://www.python.org/dev/peps/pep-3155/ RedirectTemp /pep3333 https://www.python.org/dev/peps/pep-3333/ -RedirectTemp /pypydif https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types -RedirectTemp /shed4051 https://github.com/python/typeshed/issues/4051 -RedirectTemp /slatkin https://effectivepython.com/ -RedirectTemp /specattr https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine -RedirectTemp /typing https://docs.python.org/3/library/typing.html -RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ # Remaining URLs by chapter @@ -114,18 +118,14 @@ RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ RedirectTemp /p-1 https://mail.python.org/pipermail/python-list/2002-December/134521.html RedirectTemp /p-2 https://docs.python.org/3.10/tutorial/ RedirectTemp /p-3 https://docs.python.org/3/tutorial/ -RedirectTemp /p-9 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ -RedirectTemp /p-10 https://www.oreilly.com/online-learning/try-now.html -RedirectTemp /p-13 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ -RedirectTemp /p-14 https://www.oreilly.com/ -RedirectTemp /p-15 https://www.facebook.com/OReilly/ -RedirectTemp /p-16 https://twitter.com/oreillymedia -RedirectTemp /p-17 https://www.youtube.com/oreillymedia -RedirectTemp /p-18 https://stackoverflow.com/users/95810/alex-martelli -RedirectTemp /p-19 https://pythonpro.com.br -RedirectTemp /p-20 https://groups.google.com/g/python-brasil -RedirectTemp /p-21 https://www.coffeelab.com.br/ -RedirectTemp /p-22 https://garoa.net.br/wiki/P%C3%A1gina_principal +RedirectTemp /p-4 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ +RedirectTemp /p-5 https://www.oreilly.com/online-learning/try-now.html +RedirectTemp /p-6 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ +RedirectTemp /p-7 https://stackoverflow.com/users/95810/alex-martelli +RedirectTemp /p-8 https://pythonpro.com.br +RedirectTemp /p-9 https://groups.google.com/g/python-brasil +RedirectTemp /p-10 https://www.coffeelab.com.br/ +RedirectTemp /p-11 https://garoa.net.br/wiki/P%C3%A1gina_principal ############################################################ a RedirectTemp /a-1 https://groups.google.com/forum/#!topic/python-tulip/Y4bhLNbKs74 RedirectTemp /a-2 https://docs.python.org/3/library/asyncio-eventloop.html#executor @@ -144,133 +144,145 @@ RedirectTemp /a-14 https://docs.mongodb.com/manual/about/#about-the-documentatio RedirectTemp /a-15 https://blog.startifact.com/posts/older/what-is-pythonic.html RedirectTemp /a-16 https://mail.python.org/pipermail/tutor/2003-October/thread.html#25930 RedirectTemp /a-17 https://mail.python.org/pipermail/python-list/2003-April/192027.html -RedirectTemp /a-19 https://www.python.org/doc/essays/ +RedirectTemp /a-18 https://www.python.org/doc/essays/ ############################################################ 01 RedirectTemp /1-1 http://hugunin.net/story_of_jython.html -RedirectTemp /1-3 https://docs.python.org/2/library/string.html#format-string-syntax -RedirectTemp /1-4 https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr -RedirectTemp /1-5 https://docs.python.org/3/library/stdtypes.html#truth -RedirectTemp /1-6 https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists +RedirectTemp /1-2 https://www.oreilly.com/library/view/jython-essentials/9781449397364/ +RedirectTemp /1-3 https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers +RedirectTemp /1-4 https://docs.python.org/2/library/string.html#format-string-syntax +RedirectTemp /1-5 https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr +RedirectTemp /1-6 https://docs.python.org/3/library/stdtypes.html#truth +RedirectTemp /1-7 https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists RedirectTemp /1-8 https://www.python.org/doc/humor/#the-zen-of-python -RedirectTemp /1-10 https://stackoverflow.com/users/95810/alex-martelli -RedirectTemp /1-11 https://en.wikipedia.org/wiki/Object_model -RedirectTemp /1-13 https://www.dourish.com/goodies/jargon.html -RedirectTemp /1-14 https://zopeinterface.readthedocs.io/en/latest/ -RedirectTemp /1-15 https://plone.org/ +RedirectTemp /1-9 https://stackoverflow.com/users/95810/alex-martelli +RedirectTemp /1-10 https://en.wikipedia.org/wiki/Object_model +RedirectTemp /1-11 https://www.dourish.com/goodies/jargon.html +RedirectTemp /1-12 https://zopeinterface.readthedocs.io/en/latest/ +RedirectTemp /1-13 https://plone.org/ ############################################################ 02 -RedirectTemp /2-4 https://github.com/fluentpython/example-code-2e/blob/master/02-array-seq/listcomp_speed.py -RedirectTemp /2-6 https://www.python.org/dev/peps/pep-3132/ -RedirectTemp /2-8 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations -RedirectTemp /2-9 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations -RedirectTemp /2-11 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching -RedirectTemp /2-12 https://docs.python.org/3.10/whatsnew/3.10.html -RedirectTemp /2-13 https://en.wikipedia.org/wiki/Switch_statement#Fallthrough -RedirectTemp /2-14 https://en.wikipedia.org/wiki/Dangling_else -RedirectTemp /2-15 https://github.com/gvanrossum/patma/blob/3ece6444ef70122876fd9f0099eb9490a2d630df/EXAMPLES.md#case-6-a-very-deep-iterable-and-type-match-with-extraction -RedirectTemp /2-16 https://github.com/fluentpython/lispy/blob/main/original/norvig/lis.py -RedirectTemp /2-17 https://norvig.com/lispy.html -RedirectTemp /2-18 https://numpy.org/doc/stable/user/quickstart.html#indexing-slicing-and-iterating -RedirectTemp /2-19 https://pythontutor.com/ -RedirectTemp /2-20 https://en.wikipedia.org/wiki/Fluent_interface -RedirectTemp /2-23 https://docs.python.org/3/library/bisect.html#bisect.insort -RedirectTemp /2-24 https://stackoverflow.com/questions/4845418/when-should-a-memoryview-be-used/ -RedirectTemp /2-26 https://www.fluentpython.com/extra/parsing-binary-struct/ -RedirectTemp /2-27 http://www.netlib.org -RedirectTemp /2-28 https://pandas.pydata.org/ -RedirectTemp /2-29 https://scikit-learn.org/stable/ -RedirectTemp /2-32 https://docs.python.org/3/howto/sorting.html -RedirectTemp /2-33 https://www.python.org/dev/peps/pep-3132/ -RedirectTemp /2-34 https://bugs.python.org/issue2292 -RedirectTemp /2-36 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching -RedirectTemp /2-37 https://docs.python.org/3.10/whatsnew/3.10.html -RedirectTemp /2-39 https://www.python.org/dev/peps/pep-0636/#appendix-a-quick-intro -RedirectTemp /2-41 https://eli.thegreenplace.net/2011/11/28/less-copies-in-python-with-the-buffer-protocol-and-memoryviews/ -RedirectTemp /2-42 https://jakevdp.github.io/PythonDataScienceHandbook/ -RedirectTemp /2-43 https://www.labri.fr/perso/nrougier/from-python-to-numpy/ -RedirectTemp /2-44 https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html -RedirectTemp /2-45 http://www.fonts101.com/fonts/view/Uncategorized/34398/Dijkstra -RedirectTemp /2-46 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types -RedirectTemp /2-47 https://en.wikipedia.org/wiki/Timsort -RedirectTemp /2-48 http://www.groklaw.net/pdf3/OraGoogle-1202.pdf -RedirectTemp /2-49 https://www.python.org/doc/humor/#id9 +RedirectTemp /2-1 https://github.com/fluentpython/example-code-2e/blob/master/02-array-seq/listcomp_speed.py +RedirectTemp /2-2 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /2-3 https://stackoverflow.com/questions/68630/are-tuples-more-efficient-than-lists-in-python/22140115#22140115 +RedirectTemp /2-4 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations +RedirectTemp /2-5 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations +RedirectTemp /2-6 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching +RedirectTemp /2-7 https://docs.python.org/3.10/whatsnew/3.10.html +RedirectTemp /2-8 https://en.wikipedia.org/wiki/Switch_statement#Fallthrough +RedirectTemp /2-9 https://en.wikipedia.org/wiki/Dangling_else +RedirectTemp /2-10 https://github.com/gvanrossum/patma/blob/3ece6444ef70122876fd9f0099eb9490a2d630df/EXAMPLES.md#case-6-a-very-deep-iterable-and-type-match-with-extraction +RedirectTemp /2-11 https://github.com/fluentpython/lispy/blob/main/original/norvig/lis.py +RedirectTemp /2-12 https://norvig.com/lispy.html +RedirectTemp /2-13 https://numpy.org/doc/stable/user/quickstart.html#indexing-slicing-and-iterating +RedirectTemp /2-14 https://pythontutor.com/ +RedirectTemp /2-15 https://en.wikipedia.org/wiki/Fluent_interface +RedirectTemp /2-16 https://docs.python.org/3/library/bisect.html#bisect.insort +RedirectTemp /2-17 https://stackoverflow.com/questions/4845418/when-should-a-memoryview-be-used/ +RedirectTemp /2-18 https://www.fluentpython.com/extra/parsing-binary-struct/ +RedirectTemp /2-19 http://www.netlib.org +RedirectTemp /2-20 https://pandas.pydata.org/ +RedirectTemp /2-21 https://scikit-learn.org/stable/ +RedirectTemp /2-22 https://docs.python.org/3/howto/sorting.html +RedirectTemp /2-23 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /2-24 https://bugs.python.org/issue2292 +RedirectTemp /2-25 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching +RedirectTemp /2-26 https://docs.python.org/3.10/whatsnew/3.10.html +RedirectTemp /2-27 https://www.python.org/dev/peps/pep-0636/#appendix-a-quick-intro +RedirectTemp /2-28 https://eli.thegreenplace.net/2011/11/28/less-copies-in-python-with-the-buffer-protocol-and-memoryviews/ +RedirectTemp /2-29 https://jakevdp.github.io/PythonDataScienceHandbook/ +RedirectTemp /2-30 https://www.oreilly.com/library/view/python-for-data/9781491957653/ +RedirectTemp /2-31 https://www.labri.fr/perso/nrougier/from-python-to-numpy/ +RedirectTemp /2-32 https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html +RedirectTemp /2-33 http://www.fonts101.com/fonts/view/Uncategorized/34398/Dijkstra +RedirectTemp /2-34 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types +RedirectTemp /2-35 https://en.wikipedia.org/wiki/Timsort +RedirectTemp /2-36 http://www.groklaw.net/pdf3/OraGoogle-1202.pdf +RedirectTemp /2-37 https://www.python.org/doc/humor/#id9 ############################################################ 03 -RedirectTemp /3-5 https://www.python.org/dev/peps/pep-0584/#motivation -RedirectTemp /3-7 https://docs.python.org/3.10/c-api/typeobj.html#Py_TPFLAGS_MAPPING -RedirectTemp /3-8 https://docs.python.org/3/glossary.html#term-hashable -RedirectTemp /3-9 https://docs.python.org/3/glossary.html#term-hashable -RedirectTemp /3-11 http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf -RedirectTemp /3-12 https://github.com/pingo-io/pingo-py -RedirectTemp /3-13 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/missing.py -RedirectTemp /3-14 https://docs.python.org/3/library/collections.html#collections.ChainMap -RedirectTemp /3-15 https://docs.python.org/3/library/collections.html#collections.Counter -RedirectTemp /3-16 https://docs.python.org/3/library/shelve.html -RedirectTemp /3-17 https://docs.python.org/3/library/dbm.html -RedirectTemp /3-18 https://docs.python.org/3/library/pickle.html -RedirectTemp /3-19 https://nedbatchelder.com/blog/202006/pickles_nine_flaws.html -RedirectTemp /3-20 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py#L813 -RedirectTemp /3-22 https://mail.python.org/pipermail/python-dev/2015-May/140003.html -RedirectTemp /3-23 https://bugs.python.org/issue18986 -RedirectTemp /3-24 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/transformdict.py -RedirectTemp /3-33 http://gandenberger.org/2018/03/10/ordered-dicts-vs-ordereddict/ -RedirectTemp /3-35 https://www.pypy.org/ -RedirectTemp /3-36 https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html -RedirectTemp /3-37 https://www.npopov.com/2014/12/22/PHPs-new-hashtable-implementation.html -RedirectTemp /3-38 https://www.youtube.com/watch?v=66P5FMkWoVU -RedirectTemp /3-39 https://pyvideo.org/video/276/the-mighty-dictionary-55/ -RedirectTemp /3-40 https://www.youtube.com/watch?v=p33CVV29OG8 -RedirectTemp /3-41 https://docs.python.org/3/whatsnew/3.6.html#new-dict-implementation -RedirectTemp /3-43 https://www.youtube.com/watch?v=tGAngdU_8D8 -RedirectTemp /3-44 https://speakerdeck.com/ramalho/python-set-practice-at-pycon -RedirectTemp /3-45 https://github.com/standupdev/uintset -RedirectTemp /3-46 http://www.json.org/fatfree.html -RedirectTemp /3-47 https://twitter.com/mitsuhiko/status/1229385843585974272 +RedirectTemp /3-1 https://www.python.org/dev/peps/pep-0584/#motivation +RedirectTemp /3-2 https://docs.python.org/3.10/c-api/typeobj.html#Py_TPFLAGS_MAPPING +RedirectTemp /3-3 https://docs.python.org/3/glossary.html#term-hashable +RedirectTemp /3-4 https://docs.python.org/3/glossary.html#term-hashable +RedirectTemp /3-5 http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf +RedirectTemp /3-6 https://github.com/pingo-io/pingo-py +RedirectTemp /3-7 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/missing.py +RedirectTemp /3-8 https://docs.python.org/3/library/collections.html#collections.ChainMap +RedirectTemp /3-9 https://docs.python.org/3/library/collections.html#collections.Counter +RedirectTemp /3-10 https://docs.python.org/3/library/shelve.html +RedirectTemp /3-11 https://docs.python.org/3/library/dbm.html +RedirectTemp /3-12 https://docs.python.org/3/library/pickle.html +RedirectTemp /3-13 https://nedbatchelder.com/blog/202006/pickles_nine_flaws.html +RedirectTemp /3-14 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py#L813 +RedirectTemp /3-15 https://mail.python.org/pipermail/python-dev/2015-May/140003.html +RedirectTemp /3-16 https://bugs.python.org/issue18986 +RedirectTemp /3-17 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/transformdict.py +RedirectTemp /3-18 http://gandenberger.org/2018/03/10/ordered-dicts-vs-ordereddict/ +RedirectTemp /3-19 https://www.pypy.org/ +RedirectTemp /3-20 https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html +RedirectTemp /3-21 https://www.npopov.com/2014/12/22/PHPs-new-hashtable-implementation.html +RedirectTemp /3-22 https://www.youtube.com/watch?v=66P5FMkWoVU +RedirectTemp /3-23 https://pyvideo.org/video/276/the-mighty-dictionary-55/ +RedirectTemp /3-24 https://www.youtube.com/watch?v=p33CVV29OG8 +RedirectTemp /3-25 https://docs.python.org/3/whatsnew/3.6.html#new-dict-implementation +RedirectTemp /3-26 https://github.com/python/cpython/blob/cf7eaa4617295747ee5646c4e2b7e7a16d7c64ab/Objects/dictobject.c +RedirectTemp /3-27 https://github.com/python/cpython/blob/cf7eaa4617295747ee5646c4e2b7e7a16d7c64ab/Objects/dictnotes.txt +RedirectTemp /3-28 https://www.youtube.com/watch?v=tGAngdU_8D8 +RedirectTemp /3-29 https://speakerdeck.com/ramalho/python-set-practice-at-pycon +RedirectTemp /3-30 https://github.com/standupdev/uintset +RedirectTemp /3-31 https://spectrum.ieee.org/hans-peter-luhn-and-the-birth-of-the-hashing-algorithm +RedirectTemp /3-32 http://www.json.org/fatfree.html +RedirectTemp /3-33 https://twitter.com/mitsuhiko/status/1229385843585974272 ############################################################ 04 RedirectTemp /4-1 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 RedirectTemp /4-2 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ RedirectTemp /4-3 https://www.fluentpython.com/extra/parsing-binary-struct/ -RedirectTemp /4-5 https://www.fluentpython.com/extra/multi-character-emojis/ -RedirectTemp /4-8 https://w3techs.com/technologies/overview/character_encoding -RedirectTemp /4-9 https://docs.python.org/3/library/codecs.html#codecs.register_error -RedirectTemp /4-10 https://docs.python.org/3/library/stdtypes.html#str.isascii -RedirectTemp /4-11 https://pypi.org/project/chardet/ -RedirectTemp /4-12 https://docs.python.org/3/library/codecs.html#encodings-and-unicode -RedirectTemp /4-13 https://nedbatchelder.com/text/unipain/unipain.html -RedirectTemp /4-16 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONIOENCODING -RedirectTemp /4-17 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONLEGACYWINDOWSSTDIO -RedirectTemp /4-18 https://docs.python.org/3/library/locale.html#locale.getpreferredencoding -RedirectTemp /4-19 http://www.w3.org/TR/charmod-norm/ -RedirectTemp /4-20 https://docs.python.org/3/library/locale.html?highlight=strxfrm#locale.strxfrm -RedirectTemp /4-21 https://github.com/jtauber/pyuca -RedirectTemp /4-22 http://www.unicode.org/Public/UCA/6.3.0/allkeys.txt -RedirectTemp /4-23 https://pypi.org/project/PyICU/ -RedirectTemp /4-24 https://docs.python.org/3.10/library/stdtypes.html#str.isalpha -RedirectTemp /4-25 https://en.wikipedia.org/wiki/Unicode_character_property#General_Category -RedirectTemp /4-26 https://en.wikipedia.org/wiki/Unicode_character_property -RedirectTemp /4-27 https://github.com/microsoft/terminal -RedirectTemp /4-28 https://docs.python.org/3/library/unicodedata.html -RedirectTemp /4-29 https://docs.python.org/3/reference/lexical_analysis.html#string-literal-concatenation -RedirectTemp /4-30 https://docs.python.org/3/library/re.html -RedirectTemp /4-31 https://nedbatchelder.com/text/unipain.html -RedirectTemp /4-32 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 -RedirectTemp /4-33 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ -RedirectTemp /4-34 https://regebro.wordpress.com/2011/03/23/unconfusing-unicode-what-is-unicode/ -RedirectTemp /4-35 https://docs.python.org/3/howto/unicode.html -RedirectTemp /4-36 https://diveintopython3.net/strings.html -RedirectTemp /4-37 https://diveintopython3.net/ -RedirectTemp /4-38 https://finderiko.com/python-book -RedirectTemp /4-39 https://docs.python.org/3.0/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit -RedirectTemp /4-40 https://lucumr.pocoo.org/2013/7/2/the-updated-guide-to-unicode/ -RedirectTemp /4-41 http://python-notes.curiousefficiency.org/en/latest/python3/binary_protocols.html -RedirectTemp /4-42 http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html -RedirectTemp /4-43 https://docs.python.org/3/library/codecs.html#standard-encodings -RedirectTemp /4-44 https://www.informit.com/store/unicode-demystified-a-practical-programmers-guide-to-9780201700527 -RedirectTemp /4-45 https://unicodebook.readthedocs.io/index.html -RedirectTemp /4-46 https://www.w3.org/International/wiki/Case_folding -RedirectTemp /4-47 http://www.w3.org/TR/charmod-norm/ -RedirectTemp /4-48 http://unicode.org/reports/tr15/ -RedirectTemp /4-49 http://www.unicode.org/faq/normalization.html -RedirectTemp /4-50 http://www.unicode.org/ -RedirectTemp /4-51 http://www.macchiato.com/unicode/nfc-faq +RedirectTemp /4-4 https://www.fluentpython.com/extra/multi-character-emojis/ +RedirectTemp /4-5 https://w3techs.com/technologies/overview/character_encoding +RedirectTemp /4-6 https://docs.python.org/3/library/codecs.html#codecs.register_error +RedirectTemp /4-7 https://docs.python.org/3/library/stdtypes.html#str.isascii +RedirectTemp /4-8 https://pypi.org/project/chardet/ +RedirectTemp /4-9 https://docs.python.org/3/library/codecs.html#encodings-and-unicode +RedirectTemp /4-10 https://nedbatchelder.com/text/unipain/unipain.html +RedirectTemp /4-11 https://devblogs.microsoft.com/commandline/windows-command-line-unicode-and-utf-8-output-text-buffer/ +RedirectTemp /4-12 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONIOENCODING +RedirectTemp /4-13 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONLEGACYWINDOWSSTDIO +RedirectTemp /4-14 https://docs.python.org/3/library/locale.html#locale.getpreferredencoding +RedirectTemp /4-15 http://www.w3.org/TR/charmod-norm/ +RedirectTemp /4-16 https://docs.python.org/3/library/locale.html?highlight=strxfrm#locale.strxfrm +RedirectTemp /4-17 https://pypi.org/project/pyuca/ +RedirectTemp /4-18 https://github.com/jtauber/pyuca +RedirectTemp /4-19 http://www.unicode.org/Public/UCA/6.3.0/allkeys.txt +RedirectTemp /4-20 https://pypi.org/project/PyICU/ +RedirectTemp /4-21 https://docs.python.org/3.10/library/stdtypes.html#str.isalpha +RedirectTemp /4-22 https://en.wikipedia.org/wiki/Unicode_character_property#General_Category +RedirectTemp /4-23 https://en.wikipedia.org/wiki/Unicode_character_property +RedirectTemp /4-24 https://github.com/microsoft/terminal +RedirectTemp /4-25 https://docs.python.org/3/library/unicodedata.html +RedirectTemp /4-26 https://docs.python.org/3/reference/lexical_analysis.html#string-literal-concatenation +RedirectTemp /4-27 https://docs.python.org/3/library/re.html +RedirectTemp /4-28 https://nedbatchelder.com/text/unipain.html +RedirectTemp /4-29 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 +RedirectTemp /4-30 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ +RedirectTemp /4-31 https://regebro.wordpress.com/2011/03/23/unconfusing-unicode-what-is-unicode/ +RedirectTemp /4-32 https://docs.python.org/3/howto/unicode.html +RedirectTemp /4-33 https://diveintopython3.net/strings.html +RedirectTemp /4-34 https://diveintopython3.net/ +RedirectTemp /4-35 https://finderiko.com/python-book +RedirectTemp /4-36 https://docs.python.org/3.0/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit +RedirectTemp /4-37 https://lucumr.pocoo.org/2013/7/2/the-updated-guide-to-unicode/ +RedirectTemp /4-38 http://python-notes.curiousefficiency.org/en/latest/python3/binary_protocols.html +RedirectTemp /4-39 http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html +RedirectTemp /4-40 https://docs.python.org/3/library/codecs.html#standard-encodings +RedirectTemp /4-41 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Tools/unicode/listcodecs.py +RedirectTemp /4-42 https://www.oreilly.com/library/view/unicode-explained/059610121X/ +RedirectTemp /4-43 https://www.informit.com/store/unicode-demystified-a-practical-programmers-guide-to-9780201700527 +RedirectTemp /4-44 https://unicodebook.readthedocs.io/index.html +RedirectTemp /4-45 https://www.w3.org/International/wiki/Case_folding +RedirectTemp /4-46 http://www.w3.org/TR/charmod-norm/ +RedirectTemp /4-47 http://unicode.org/reports/tr15/ +RedirectTemp /4-48 http://www.unicode.org/faq/normalization.html +RedirectTemp /4-49 http://www.unicode.org/ +RedirectTemp /4-50 http://www.macchiato.com/unicode/nfc-faq +RedirectTemp /4-51 https://stories.moma.org/the-original-emoji-set-has-been-added-to-the-museum-of-modern-arts-collection-c6060e141f61?gi=c403f5a840a5 RedirectTemp /4-52 https://emojipedia.org/ RedirectTemp /4-53 https://blog.emojipedia.org/correcting-the-record-on-the-first-emoji-set/ RedirectTemp /4-54 http://emojitracker.com/ @@ -279,397 +291,433 @@ RedirectTemp /4-56 http://www.methods.co.nz/asciidoc/ RedirectTemp /4-57 https://atlas.oreilly.com/ ############################################################ 05 RedirectTemp /5-1 https://docs.python.org/3/library/typing.html#typing.TypedDict -RedirectTemp /5-4 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations -RedirectTemp /5-5 https://docs.python.org/3/library/typing.html#typing.get_type_hints -RedirectTemp /5-6 https://docs.python.org/3.8/library/collections.html#collections.somenamedtuple._asdict -RedirectTemp /5-8 https://www.jetbrains.com/pycharm/ -RedirectTemp /5-10 https://www.python.org/dev/peps/pep-0484/#acceptable-type-hints -RedirectTemp /5-11 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass -RedirectTemp /5-13 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass -RedirectTemp /5-14 https://docs.python.org/3/library/dataclasses.html -RedirectTemp /5-15 https://docs.python.org/3/library/dataclasses.html#inheritance -RedirectTemp /5-16 https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations -RedirectTemp /5-22 https://dublincore.org/specifications/dublin-core/ -RedirectTemp /5-23 https://en.wikipedia.org/wiki/Dublin_Core -RedirectTemp /5-24 https://martinfowler.com/bliki/CodeSmell.html -RedirectTemp /5-25 https://martinfowler.com/books/refactoring.html -RedirectTemp /5-26 https://www.python.org/dev/peps/pep-0634/#class-patterns -RedirectTemp /5-30 https://docs.python.org/3/library/dataclasses.html -RedirectTemp /5-32 https://www.python.org/dev/peps/pep-0557/#id47 -RedirectTemp /5-33 https://www.python.org/dev/peps/pep-0557/#id48 -RedirectTemp /5-34 https://www.python.org/dev/peps/pep-0557/#id33 -RedirectTemp /5-35 https://realpython.com -RedirectTemp /5-36 https://realpython.com/python-data-classes/ -RedirectTemp /5-37 https://www.youtube.com/watch?v=T-TwcmT6Rcw -RedirectTemp /5-38 https://www.attrs.org/en/stable/ -RedirectTemp /5-39 https://glyph.twistedmatrix.com/2016/08/attrs.html -RedirectTemp /5-40 https://www.attrs.org/en/stable/why.html -RedirectTemp /5-41 https://github.com/dabeaz/cluegen -RedirectTemp /5-42 https://refactoring.guru/ -RedirectTemp /5-43 https://refactoring.guru/smells/data-class -RedirectTemp /5-44 https://web.archive.org/web/20190204130328/http://catb.org/esr/jargon/html/G/Guido.html -RedirectTemp /5-45 https://web.archive.org/web/20190211161610/http://catb.org/esr/jargon/html/index.html -RedirectTemp /5-47 https://www.attrs.org/en/stable/ +RedirectTemp /5-2 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations +RedirectTemp /5-3 https://docs.python.org/3/library/typing.html#typing.get_type_hints +RedirectTemp /5-4 https://docs.python.org/3.8/library/collections.html#collections.somenamedtuple._asdict +RedirectTemp /5-5 https://www.jetbrains.com/pycharm/ +RedirectTemp /5-6 https://www.python.org/dev/peps/pep-0484/#acceptable-type-hints +RedirectTemp /5-7 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass +RedirectTemp /5-8 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass +RedirectTemp /5-9 https://docs.python.org/3/library/dataclasses.html +RedirectTemp /5-10 https://docs.python.org/3/library/dataclasses.html#inheritance +RedirectTemp /5-11 https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations +RedirectTemp /5-12 https://dublincore.org/specifications/dublin-core/ +RedirectTemp /5-13 https://en.wikipedia.org/wiki/Dublin_Core +RedirectTemp /5-14 https://martinfowler.com/bliki/CodeSmell.html +RedirectTemp /5-15 https://martinfowler.com/books/refactoring.html +RedirectTemp /5-16 https://www.python.org/dev/peps/pep-0634/#class-patterns +RedirectTemp /5-17 https://docs.python.org/3/library/dataclasses.html +RedirectTemp /5-18 https://www.python.org/dev/peps/pep-0557/#id47 +RedirectTemp /5-19 https://www.python.org/dev/peps/pep-0557/#id48 +RedirectTemp /5-20 https://www.python.org/dev/peps/pep-0557/#id33 +RedirectTemp /5-21 https://realpython.com +RedirectTemp /5-22 https://realpython.com/python-data-classes/ +RedirectTemp /5-23 https://www.youtube.com/watch?v=T-TwcmT6Rcw +RedirectTemp /5-24 https://www.attrs.org/en/stable/ +RedirectTemp /5-25 https://glyph.twistedmatrix.com/2016/08/attrs.html +RedirectTemp /5-26 https://www.attrs.org/en/stable/why.html +RedirectTemp /5-27 https://github.com/dabeaz/cluegen +RedirectTemp /5-28 https://refactoring.guru/ +RedirectTemp /5-29 https://refactoring.guru/smells/data-class +RedirectTemp /5-30 https://web.archive.org/web/20190204130328/http://catb.org/esr/jargon/html/G/Guido.html +RedirectTemp /5-31 https://web.archive.org/web/20190211161610/http://catb.org/esr/jargon/html/index.html +RedirectTemp /5-32 https://www.attrs.org/en/stable/ ############################################################ 06 -RedirectTemp /6-3 https://www.olin.edu/faculty/profile/lynn-andrea-stein/ -RedirectTemp /6-4 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types -RedirectTemp /6-5 https://pythontutor.com/ -RedirectTemp /6-6 https://docs.python.org/3/library/copy.html -RedirectTemp /6-7 https://en.wikipedia.org/wiki/Principle_of_least_astonishment -RedirectTemp /6-8 https://docs.python.org/3/reference/datamodel.html#object.%5C_%5C_del__ -RedirectTemp /6-9 https://emptysqua.re/blog/pypy-garbage-collection-and-a-deadlock/ -RedirectTemp /6-15 https://www.youtube.com/watch?v=HHFCFJSPWrI&feature=youtu.be -RedirectTemp /6-16 http://pymotw.com/3/copy/ -RedirectTemp /6-17 http://pymotw.com/3/weakref/ -RedirectTemp /6-18 https://docs.python.org/3/library/gc.html -RedirectTemp /6-20 https://devguide.python.org/garbage_collector/ -RedirectTemp /6-21 https://devguide.python.org/ -RedirectTemp /6-22 https://www.python.org/dev/peps/pep-0442/ -RedirectTemp /6-23 https://en.wikipedia.org/wiki/String_interning -RedirectTemp /6-24 https://en.wikipedia.org/wiki/Haddocks%27_Eyes -RedirectTemp /6-25 https://thp.io/2012/python-gc/python_gc_final_2012-01-22.pdf +RedirectTemp /6-1 https://www.olin.edu/faculty/profile/lynn-andrea-stein/ +RedirectTemp /6-2 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types +RedirectTemp /6-3 https://pythontutor.com/ +RedirectTemp /6-4 https://docs.python.org/3/library/copy.html +RedirectTemp /6-5 https://en.wikipedia.org/wiki/Principle_of_least_astonishment +RedirectTemp /6-6 https://docs.python.org/3/reference/datamodel.html#object.%5C_%5C_del__ +RedirectTemp /6-7 https://emptysqua.re/blog/pypy-garbage-collection-and-a-deadlock/ +RedirectTemp /6-8 https://www.youtube.com/watch?v=HHFCFJSPWrI&feature=youtu.be +RedirectTemp /6-9 http://pymotw.com/3/copy/ +RedirectTemp /6-10 http://pymotw.com/3/weakref/ +RedirectTemp /6-11 https://docs.python.org/3/library/gc.html +RedirectTemp /6-12 https://devguide.python.org/garbage_collector/ +RedirectTemp /6-13 https://devguide.python.org/ +RedirectTemp /6-14 https://www.python.org/dev/peps/pep-0442/ +RedirectTemp /6-15 https://en.wikipedia.org/wiki/String_interning +RedirectTemp /6-16 https://en.wikipedia.org/wiki/Haddocks%27_Eyes +RedirectTemp /6-17 https://thp.io/2012/python-gc/python_gc_final_2012-01-22.pdf ############################################################ 07 RedirectTemp /7-1 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html -RedirectTemp /7-3 https://www.fluentpython.com/extra/function-introspection/ -RedirectTemp /7-5 https://docs.python.org/3/library/functions.html#map -RedirectTemp /7-6 https://en.wikipedia.org/wiki/Functional_programming -RedirectTemp /7-7 https://docs.python.org/3/howto/functional.html -RedirectTemp /7-8 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy -RedirectTemp /7-9 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters -RedirectTemp /7-10 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters -RedirectTemp /7-12 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy -RedirectTemp /7-14 https://docs.python.org/3/howto/functional.html -RedirectTemp /7-15 https://stackoverflow.com/questions/3252228/python-why-is-functools-partial-necessary -RedirectTemp /7-16 https://speakerdeck.com/ramalho/beyond-paradigms-berlin-edition -RedirectTemp /7-17 https://www.youtube.com/watch?v=bF3a2VYXxa0 -RedirectTemp /7-18 http://cs.brown.edu/~sk/Publications/Papers/Published/sk-teach-pl-post-linnaean/ -RedirectTemp /7-19 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html -RedirectTemp /7-20 https://raw.githubusercontent.com/python/cpython/main/Misc/HISTORY -RedirectTemp /7-21 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html +RedirectTemp /7-2 https://www.fluentpython.com/extra/function-introspection/ +RedirectTemp /7-3 https://docs.python.org/3/library/functions.html#map +RedirectTemp /7-4 https://en.wikipedia.org/wiki/Functional_programming +RedirectTemp /7-5 https://docs.python.org/3/howto/functional.html +RedirectTemp /7-6 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy +RedirectTemp /7-7 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters +RedirectTemp /7-8 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters +RedirectTemp /7-9 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/functools.py#L341 +RedirectTemp /7-10 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy +RedirectTemp /7-11 https://docs.python.org/3/howto/functional.html +RedirectTemp /7-12 https://stackoverflow.com/questions/3252228/python-why-is-functools-partial-necessary +RedirectTemp /7-13 https://speakerdeck.com/ramalho/beyond-paradigms-berlin-edition +RedirectTemp /7-14 https://www.youtube.com/watch?v=bF3a2VYXxa0 +RedirectTemp /7-15 http://cs.brown.edu/~sk/Publications/Papers/Published/sk-teach-pl-post-linnaean/ +RedirectTemp /7-16 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html +RedirectTemp /7-17 https://raw.githubusercontent.com/python/cpython/main/Misc/HISTORY +RedirectTemp /7-18 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html ############################################################ 08 RedirectTemp /8-1 https://www.python.org/dev/peps/pep-0484/#non-goals -RedirectTemp /8-4 https://github.com/python/typing/issues/182 -RedirectTemp /8-5 https://github.com/python/mypy/issues/731 -RedirectTemp /8-6 https://github.com/google/pytype -RedirectTemp /8-7 https://github.com/Microsoft/pyright -RedirectTemp /8-8 https://pyre-check.org/ -RedirectTemp /8-10 https://mypy.readthedocs.io/en/stable/introduction.html -RedirectTemp /8-11 https://mypy.readthedocs.io/en/stable/config_file.html -RedirectTemp /8-12 https://pypi.org/project/flake8/ -RedirectTemp /8-13 https://pypi.org/project/blue/ -RedirectTemp /8-14 https://pypi.org/project/black/ -RedirectTemp /8-16 https://wefearchange.org/2020/11/steeringcouncil.rst.html -RedirectTemp /8-17 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes -RedirectTemp /8-18 https://en.wikipedia.org/wiki/Barbara_Liskov -RedirectTemp /8-19 https://en.wikipedia.org/wiki/Behavioral_subtyping -RedirectTemp /8-22 https://www.python.org/dev/peps/pep-0585/#implementation -RedirectTemp /8-25 https://docs.python.org/3/library/typing.html#module-contents -RedirectTemp /8-26 https://en.wikipedia.org/wiki/Geohash -RedirectTemp /8-27 https://en.wikipedia.org/wiki/Inverted_index -RedirectTemp /8-29 https://docs.python.org/3/library/typing.html#typing.List -RedirectTemp /8-30 https://docs.python.org/3/library/typing.html#typing.Dict -RedirectTemp /8-31 https://docs.python.org/3/library/typing.html#typing.Set -RedirectTemp /8-32 https://www.python.org/dev/peps/pep-0585/#implementation -RedirectTemp /8-34 https://docs.python.org/3/library/numbers.html -RedirectTemp /8-37 https://docs.python.org/3/library/typing.html#typing.List -RedirectTemp /8-38 https://github.com/python/typeshed -RedirectTemp /8-39 https://github.com/python/typeshed/blob/66cd36268a6a667714efaa27198a41d0d7f89477/stdlib/2and3/math.pyi#L45 -RedirectTemp /8-41 https://docs.python.org/3/library/statistics.html#statistics.mode -RedirectTemp /8-42 https://docs.python.org/3/library/statistics.html#statistics.mode -RedirectTemp /8-46 https://docs.python.org/3/library/typing.html#typing.Callable -RedirectTemp /8-47 https://pypi.org/project/blue/ -RedirectTemp /8-48 https://www.python.org/dev/peps/pep-0484/#id38 -RedirectTemp /8-49 https://docs.google.com/document/d/1aXs1tpwzPjW9MdsG5dI7clNFyYayFBkcXwRDo-qvbIk/preview -RedirectTemp /8-50 https://www.oreilly.com/library/view/the-best-software/9781590595008/ -RedirectTemp /8-51 https://www.youtube.com/watch?v=YFexUDjHO6w -RedirectTemp /8-52 https://www.youtube.com/watch?v=YFexUDjHO6w&t=13m40s -RedirectTemp /8-53 https://bernat.tech/posts/the-state-of-type-hints-in-python/ -RedirectTemp /8-54 https://realpython.com/python-type-checking/ -RedirectTemp /8-55 https://cjolowicz.github.io/posts/hypermodern-python-04-typing/ -RedirectTemp /8-56 https://mypy.readthedocs.io/en/stable/index.html -RedirectTemp /8-57 https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html -RedirectTemp /8-58 https://mypy.readthedocs.io/en/stable/common_issues.html -RedirectTemp /8-60 https://github.com/typeddjango/awesome-python-typing -RedirectTemp /8-61 https://docs.python.org/3/library/functions.html#max -RedirectTemp /8-62 https://en.wikipedia.org/wiki/Linguistic_relativity -RedirectTemp /8-63 https://pypistats.org/top -RedirectTemp /8-64 https://github.com/psf/requests/issues/3855 -RedirectTemp /8-65 https://lwn.net/Articles/643399/ -RedirectTemp /8-66 https://docs.python-requests.org/en/master/api/#requests.request -RedirectTemp /8-67 https://queue.acm.org/detail.cfm?id=1039523 +RedirectTemp /8-2 https://github.com/python/typing/issues/182 +RedirectTemp /8-3 https://github.com/python/mypy/issues/731 +RedirectTemp /8-4 https://github.com/google/pytype +RedirectTemp /8-5 https://github.com/Microsoft/pyright +RedirectTemp /8-6 https://pyre-check.org/ +RedirectTemp /8-7 https://mypy.readthedocs.io/en/stable/introduction.html +RedirectTemp /8-8 https://mypy.readthedocs.io/en/stable/config_file.html +RedirectTemp /8-9 https://pypi.org/project/flake8/ +RedirectTemp /8-10 https://pypi.org/project/blue/ +RedirectTemp /8-11 https://pypi.org/project/black/ +RedirectTemp /8-12 https://wefearchange.org/2020/11/steeringcouncil.rst.html +RedirectTemp /8-13 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes +RedirectTemp /8-14 https://en.wikipedia.org/wiki/Barbara_Liskov +RedirectTemp /8-15 https://en.wikipedia.org/wiki/Behavioral_subtyping +RedirectTemp /8-16 https://www.python.org/dev/peps/pep-0585/#implementation +RedirectTemp /8-17 https://docs.python.org/3/library/typing.html#module-contents +RedirectTemp /8-18 https://en.wikipedia.org/wiki/Geohash +RedirectTemp /8-19 https://en.wikipedia.org/wiki/Inverted_index +RedirectTemp /8-20 https://docs.python.org/3/library/typing.html#typing.List +RedirectTemp /8-21 https://docs.python.org/3/library/typing.html#typing.Dict +RedirectTemp /8-22 https://docs.python.org/3/library/typing.html#typing.Set +RedirectTemp /8-23 https://www.python.org/dev/peps/pep-0585/#implementation +RedirectTemp /8-24 https://docs.python.org/3/library/numbers.html +RedirectTemp /8-25 https://docs.python.org/3/library/typing.html#typing.List +RedirectTemp /8-26 https://github.com/python/typeshed +RedirectTemp /8-27 https://github.com/python/typeshed/blob/66cd36268a6a667714efaa27198a41d0d7f89477/stdlib/2and3/math.pyi#L45 +RedirectTemp /8-28 https://docs.python.org/3/library/statistics.html#statistics.mode +RedirectTemp /8-29 https://github.com/python/cpython/blob/822efa5695b5ba6c2316c1400e4e9ec2546f7ea5/Lib/statistics.py#L534 +RedirectTemp /8-30 https://github.com/python/typeshed/blob/e1e99245bb46223928eba68d4fc74962240ba5b4/stdlib/3/statistics.pyi +RedirectTemp /8-31 https://docs.python.org/3/library/statistics.html#statistics.mode +RedirectTemp /8-32 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/3/statistics.pyi#L32 +RedirectTemp /8-33 https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ +RedirectTemp /8-34 https://docs.python.org/3/library/typing.html#typing.Callable +RedirectTemp /8-35 https://pypi.org/project/blue/ +RedirectTemp /8-36 https://www.python.org/dev/peps/pep-0484/#id38 +RedirectTemp /8-37 https://docs.google.com/document/d/1aXs1tpwzPjW9MdsG5dI7clNFyYayFBkcXwRDo-qvbIk/preview +RedirectTemp /8-38 https://www.oreilly.com/library/view/the-best-software/9781590595008/ +RedirectTemp /8-39 https://www.youtube.com/watch?v=YFexUDjHO6w +RedirectTemp /8-40 https://www.youtube.com/watch?v=YFexUDjHO6w&t=13m40s +RedirectTemp /8-41 https://bernat.tech/posts/the-state-of-type-hints-in-python/ +RedirectTemp /8-42 https://realpython.com/python-type-checking/ +RedirectTemp /8-43 https://cjolowicz.github.io/posts/hypermodern-python-04-typing/ +RedirectTemp /8-44 https://mypy.readthedocs.io/en/stable/index.html +RedirectTemp /8-45 https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html +RedirectTemp /8-46 https://mypy.readthedocs.io/en/stable/common_issues.html +RedirectTemp /8-47 https://github.com/typeddjango/awesome-python-typing +RedirectTemp /8-48 https://docs.python.org/3/library/functions.html#max +RedirectTemp /8-49 https://en.wikipedia.org/wiki/Linguistic_relativity +RedirectTemp /8-50 https://pypistats.org/top +RedirectTemp /8-51 https://github.com/psf/requests/issues/3855 +RedirectTemp /8-52 https://lwn.net/Articles/643399/ +RedirectTemp /8-53 https://docs.python-requests.org/en/master/api/#requests.request +RedirectTemp /8-54 https://queue.acm.org/detail.cfm?id=1039523 ############################################################ 09 RedirectTemp /9-1 https://docs.python.org/3/library/dis.html RedirectTemp /9-2 https://en.wikipedia.org/wiki/Memoization RedirectTemp /9-3 https://numpy.org/doc/stable/user/basics.types.html -RedirectTemp /9-5 https://docs.python.org/3/library/functools.html#functools.singledispatch -RedirectTemp /9-7 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/README.md -RedirectTemp /9-8 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/01-how-you-implemented-your-python-decorator-is-wrong.md -RedirectTemp /9-9 https://wrapt.readthedocs.io/en/latest/ -RedirectTemp /9-10 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch09.html -RedirectTemp /9-11 https://pypi.org/project/decorator/ -RedirectTemp /9-12 https://wiki.python.org/moin/PythonDecoratorLibrary -RedirectTemp /9-13 http://web.archive.org/web/20201109032203/http://effbot.org/zone/closure.htm -RedirectTemp /9-14 https://www.python.org/dev/peps/pep-3104/ -RedirectTemp /9-15 https://www.python.org/dev/peps/pep-0227/ -RedirectTemp /9-16 https://www.python.org/dev/peps/pep-0443/ -RedirectTemp /9-17 https://www.artima.com/weblogs/viewpost.jsp?thread=101605 -RedirectTemp /9-18 https://reg.readthedocs.io/en/latest/ -RedirectTemp /9-19 https://morepath.readthedocs.io/en/latest/ -RedirectTemp /9-20 https://www.gnu.org/software/emacs/manual/html_node/elisp/Dynamic-Binding.html -RedirectTemp /9-21 http://www.paulgraham.com/rootsoflisp.html -RedirectTemp /9-22 http://www-formal.stanford.edu/jmc/recursive/recursive.html -RedirectTemp /9-23 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this +RedirectTemp /9-4 https://docs.python.org/3/library/functools.html#functools.singledispatch +RedirectTemp /9-5 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/README.md +RedirectTemp /9-6 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/01-how-you-implemented-your-python-decorator-is-wrong.md +RedirectTemp /9-7 https://wrapt.readthedocs.io/en/latest/ +RedirectTemp /9-8 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch09.html +RedirectTemp /9-9 https://pypi.org/project/decorator/ +RedirectTemp /9-10 https://wiki.python.org/moin/PythonDecoratorLibrary +RedirectTemp /9-11 http://web.archive.org/web/20201109032203/http://effbot.org/zone/closure.htm +RedirectTemp /9-12 https://www.python.org/dev/peps/pep-3104/ +RedirectTemp /9-13 https://www.python.org/dev/peps/pep-0227/ +RedirectTemp /9-14 https://www.python.org/dev/peps/pep-0443/ +RedirectTemp /9-15 https://www.artima.com/weblogs/viewpost.jsp?thread=101605 +RedirectTemp /9-16 https://reg.readthedocs.io/en/latest/ +RedirectTemp /9-17 https://morepath.readthedocs.io/en/latest/ +RedirectTemp /9-18 https://www.gnu.org/software/emacs/manual/html_node/elisp/Dynamic-Binding.html +RedirectTemp /9-19 http://www.paulgraham.com/rootsoflisp.html +RedirectTemp /9-20 http://www-formal.stanford.edu/jmc/recursive/recursive.html +RedirectTemp /9-21 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this ############################################################ 10 RedirectTemp /10-1 https://en.wikipedia.org/wiki/Software_design_pattern RedirectTemp /10-2 https://en.wikipedia.org/wiki/Iterator_pattern -RedirectTemp /10-4 https://github.com/python/mypy/issues/9397 -RedirectTemp /10-5 http://www.norvig.com/design-patterns/index.htm -RedirectTemp /10-6 https://pyvideo.org/video/1110/python-design-patterns/ -RedirectTemp /10-7 http://www.aleax.it/gdd_pydp.pdf -RedirectTemp /10-9 https://perl.plover.com/yak/design/ -RedirectTemp /10-11 https://en.wikipedia.org/wiki/Turtles_all_the_way_down +RedirectTemp /10-3 https://github.com/python/mypy/issues/9397 +RedirectTemp /10-4 http://www.norvig.com/design-patterns/index.htm +RedirectTemp /10-5 https://pyvideo.org/video/1110/python-design-patterns/ +RedirectTemp /10-6 http://www.aleax.it/gdd_pydp.pdf +RedirectTemp /10-7 https://perl.plover.com/yak/design/ +RedirectTemp /10-8 https://en.wikipedia.org/wiki/Turtles_all_the_way_down ############################################################ 11 RedirectTemp /11-1 https://blog.startifact.com/posts/older/what-is-pythonic.html RedirectTemp /11-2 https://julien.danjou.info/guide-python-static-class-abstract-methods/ RedirectTemp /11-3 https://docs.python.org/3/library/string.html#formatspec -RedirectTemp /11-5 https://docs.python.org/3/reference/lexical_analysis.html#f-strings -RedirectTemp /11-6 https://docs.python.org/3/library/string.html#format-string-syntax -RedirectTemp /11-7 https://docs.python.org/3/library/string.html#formatspec -RedirectTemp /11-8 https://docs.python.org/3/reference/datamodel.html#object.__hash__ -RedirectTemp /11-9 https://web.archive.org/web/20161025185040/http://pythonpaste.org/StyleGuide.html -RedirectTemp /11-10 https://docs.python.org/3/tutorial/modules.html#more-on-modules -RedirectTemp /11-11 https://docs.python.org/3/library/gettext.html#gettext.NullTranslations -RedirectTemp /11-12 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/mem_test.py -RedirectTemp /11-16 https://docs.python.org/3/reference/datamodel.html#basic-customization -RedirectTemp /11-18 http://esug.org/data/HistoricalDocuments/TheSmalltalkReport/ST07/04wo.pdf -RedirectTemp /11-19 https://docs.oracle.com/javase/tutorial/essential/environment/security.html -RedirectTemp /11-21 https://docs.oracle.com/javase/tutorial/essential/environment/security.html +RedirectTemp /11-4 https://docs.python.org/3/reference/lexical_analysis.html#f-strings +RedirectTemp /11-5 https://docs.python.org/3/library/string.html#format-string-syntax +RedirectTemp /11-6 https://docs.python.org/3/library/string.html#formatspec +RedirectTemp /11-7 https://docs.python.org/3/reference/datamodel.html#object.__hash__ +RedirectTemp /11-8 https://web.archive.org/web/20161025185040/http://pythonpaste.org/StyleGuide.html +RedirectTemp /11-9 https://docs.python.org/3/tutorial/modules.html#more-on-modules +RedirectTemp /11-10 https://docs.python.org/3/library/gettext.html#gettext.NullTranslations +RedirectTemp /11-11 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/mem_test.py +RedirectTemp /11-12 https://docs.python.org/3/reference/datamodel.html#basic-customization +RedirectTemp /11-13 http://esug.org/data/HistoricalDocuments/TheSmalltalkReport/ST07/04wo.pdf +RedirectTemp /11-14 https://www.artima.com/articles/the-simplest-thing-that-could-possibly-work#part3 +RedirectTemp /11-15 https://docs.oracle.com/javase/tutorial/essential/environment/security.html +RedirectTemp /11-16 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/private/Expose.java +RedirectTemp /11-17 https://docs.oracle.com/javase/tutorial/essential/environment/security.html ############################################################ 12 RedirectTemp /12-1 https://en.wikipedia.org/wiki/Vector_space_model RedirectTemp /12-2 https://pypi.org/project/gensim/ -RedirectTemp /12-6 https://docs.python.org/3/library/functions.html#enumerate -RedirectTemp /12-7 https://mathworld.wolfram.com/Hypersphere.html -RedirectTemp /12-12 https://en.wikipedia.org/wiki/Fold_(higher-order_function) -RedirectTemp /12-13 https://docs.python.org/2.5/whatsnew/pep-357.html -RedirectTemp /12-15 https://docs.python.org/3/reference/datamodel.html#special-method-names -RedirectTemp /12-16 https://en.wikipedia.org/wiki/KISS_principle -RedirectTemp /12-17 https://mail.python.org/pipermail/python-list/2000-July/046184.html -RedirectTemp /12-18 https://en.wikipedia.org/wiki/Duck_typing -RedirectTemp /12-19 https://mail.python.org/mailman/listinfo/python-list -RedirectTemp /12-20 https://mail.python.org/pipermail/python-list/2003-April/218568.html +RedirectTemp /12-3 https://docs.python.org/3/library/functions.html#enumerate +RedirectTemp /12-4 https://mathworld.wolfram.com/Hypersphere.html +RedirectTemp /12-5 https://en.wikipedia.org/wiki/Fold_(higher-order_function) +RedirectTemp /12-6 https://docs.python.org/2.5/whatsnew/pep-357.html +RedirectTemp /12-7 https://docs.python.org/3/reference/datamodel.html#special-method-names +RedirectTemp /12-8 https://en.wikipedia.org/wiki/KISS_principle +RedirectTemp /12-9 https://mail.python.org/pipermail/python-list/2000-July/046184.html +RedirectTemp /12-10 https://en.wikipedia.org/wiki/Duck_typing +RedirectTemp /12-11 https://mail.python.org/mailman/listinfo/python-list +RedirectTemp /12-12 https://mail.python.org/pipermail/python-list/2003-April/218568.html ############################################################ 13 -RedirectTemp /13-2 https://docs.python.org/3/c-api/index.html -RedirectTemp /13-3 https://docs.python.org/3/c-api/sequence.html -RedirectTemp /13-7 https://github.com/python/cpython/blob/31ceccb2c77854893f3a754aca04bedd74bedb10/Lib/_collections_abc.py#L870 -RedirectTemp /13-8 https://en.wikipedia.org/wiki/Monkey_patch -RedirectTemp /13-9 https://www.gevent.org/api/gevent.monkey.html -RedirectTemp /13-10 https://docs.python.org/3/library/random.html#random.shuffle -RedirectTemp /13-11 https://docs.python.org/3/reference/datamodel.html#emulating-container-types -RedirectTemp /13-12 https://docs.python.org/3/library/collections.html#collections.namedtuple -RedirectTemp /13-13 https://github.com/python/typeshed/blob/24afb531ffd07083d6a74be917342195062f7277/stdlib/collections/__init__.pyi -RedirectTemp /13-14 https://docs.python.org/3/glossary.html#term-abstract-base-class -RedirectTemp /13-15 https://en.wikipedia.org/wiki/Duck_typing#History -RedirectTemp /13-16 http://ptgmedia.pearsoncmg.com/images/020163371x/items/item33.html -RedirectTemp /13-17 https://docs.python.org/3/library/bisect.html#bisect.bisect -RedirectTemp /13-20 https://github.com/python/cpython/blob/main/Lib/_collections_abc.py -RedirectTemp /13-21 https://github.com/python/cpython/blob/main/Lib/abc.py -RedirectTemp /13-22 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes -RedirectTemp /13-23 https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable -RedirectTemp /13-24 https://docs.python.org/3/library/abc.html -RedirectTemp /13-25 https://docs.python.org/dev/library/abc.html#abc.abstractmethod -RedirectTemp /13-26 https://docs.python.org/dev/library/abc.html -RedirectTemp /13-27 https://docs.python.org/3/library/os.html#os.urandom -RedirectTemp /13-28 https://github.com/python/mypy/issues/2922 -RedirectTemp /13-29 https://docs.python.org/3/library/stdtypes.html#truth -RedirectTemp /13-30 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py -RedirectTemp /13-31 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L369 -RedirectTemp /13-32 https://bugs.python.org/issue31333 -RedirectTemp /13-33 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Modules/_abc.c#L605 -RedirectTemp /13-34 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L881 -RedirectTemp /13-37 https://docs.python.org/3/library/typing.html#protocols -RedirectTemp /13-40 https://martinfowler.com/bliki/RoleInterface.html -RedirectTemp /13-41 https://en.wikipedia.org/wiki/Interface_segregation_principle -RedirectTemp /13-42 https://github.com/python/typeshed/blob/master/CONTRIBUTING.md -RedirectTemp /13-43 https://gist.github.com/asukakenji/ac8a05644a2e98f1d5ea8c299541fce9 -RedirectTemp /13-44 https://www.python.org/dev/peps/pep-0544/#merging-and-extending-protocols -RedirectTemp /13-45 https://numpy.org/devdocs/user/basics.types.html -RedirectTemp /13-46 https://github.com/python/typeshed/blob/master/stdlib/statistics.pyi -RedirectTemp /13-47 https://bugs.python.org/issue41974 -RedirectTemp /13-49 https://glyph.twistedmatrix.com/2020/07/new-duck.html -RedirectTemp /13-50 https://glyph.twistedmatrix.com/2021/03/interfaces-and-protocols.html -RedirectTemp /13-51 https://plone.org/ -RedirectTemp /13-52 https://trypyramid.com/ -RedirectTemp /13-53 https://twistedmatrix.com/trac/ -RedirectTemp /13-54 https://www.artima.com/articles/contracts-in-python -RedirectTemp /13-55 https://martinfowler.com/bliki/DynamicTyping.html -RedirectTemp /13-56 https://martinfowler.com/bliki/RoleInterface.html -RedirectTemp /13-57 https://mypy.readthedocs.io/en/stable/protocols.html -RedirectTemp /13-58 https://pymotw.com/3/abc/index.html -RedirectTemp /13-59 https://www.python.org/dev/peps/pep-3119/ -RedirectTemp /13-60 https://www.python.org/dev/peps/pep-3141/ -RedirectTemp /13-61 https://docs.python.org/3/library/numbers.html -RedirectTemp /13-62 https://github.com/python/mypy/issues/3186 -RedirectTemp /13-63 https://github.com/python/mypy/issues/3186 -RedirectTemp /13-64 https://martinfowler.com/articles/lean-inception/ -RedirectTemp /13-65 https://martinfowler.com -RedirectTemp /13-68 https://www.jetbrains.com/pycharm/ -RedirectTemp /13-69 https://wingware.com/ -RedirectTemp /13-70 https://code.visualstudio.com/ +RedirectTemp /13-1 https://docs.python.org/3/c-api/index.html +RedirectTemp /13-2 https://docs.python.org/3/c-api/sequence.html +RedirectTemp /13-3 https://github.com/python/cpython/blob/31ceccb2c77854893f3a754aca04bedd74bedb10/Lib/_collections_abc.py#L870 +RedirectTemp /13-4 https://en.wikipedia.org/wiki/Monkey_patch +RedirectTemp /13-5 https://www.gevent.org/api/gevent.monkey.html +RedirectTemp /13-6 https://docs.python.org/3/library/random.html#random.shuffle +RedirectTemp /13-7 https://docs.python.org/3/reference/datamodel.html#emulating-container-types +RedirectTemp /13-8 https://docs.python.org/3/library/collections.html#collections.namedtuple +RedirectTemp /13-9 https://github.com/python/typeshed/blob/24afb531ffd07083d6a74be917342195062f7277/stdlib/collections/__init__.pyi +RedirectTemp /13-10 https://docs.python.org/3/glossary.html#term-abstract-base-class +RedirectTemp /13-11 https://en.wikipedia.org/wiki/Duck_typing#History +RedirectTemp /13-12 http://ptgmedia.pearsoncmg.com/images/020163371x/items/item33.html +RedirectTemp /13-13 https://docs.python.org/3/library/bisect.html#bisect.bisect +RedirectTemp /13-14 https://github.com/python/cpython/blob/main/Lib/_collections_abc.py +RedirectTemp /13-15 https://github.com/python/cpython/blob/main/Lib/abc.py +RedirectTemp /13-16 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes +RedirectTemp /13-17 https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable +RedirectTemp /13-18 https://docs.python.org/3/library/abc.html +RedirectTemp /13-19 https://docs.python.org/dev/library/abc.html#abc.abstractmethod +RedirectTemp /13-20 https://docs.python.org/dev/library/abc.html +RedirectTemp /13-21 https://docs.python.org/3/library/os.html#os.urandom +RedirectTemp /13-22 https://github.com/python/mypy/issues/2922 +RedirectTemp /13-23 https://docs.python.org/3/library/stdtypes.html#truth +RedirectTemp /13-24 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py +RedirectTemp /13-25 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L369 +RedirectTemp /13-26 https://github.com/python/cpython/blob/c0a9afe2ac1820409e6173bd1893ebee2cf50270/Lib/abc.py#L196 +RedirectTemp /13-27 https://bugs.python.org/issue31333 +RedirectTemp /13-28 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Modules/_abc.c#L605 +RedirectTemp /13-29 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L881 +RedirectTemp /13-30 https://docs.python.org/3/library/typing.html#protocols +RedirectTemp /13-31 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Lib/typing.py#L1751 +RedirectTemp /13-32 https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ +RedirectTemp /13-33 https://martinfowler.com/bliki/RoleInterface.html +RedirectTemp /13-34 https://en.wikipedia.org/wiki/Interface_segregation_principle +RedirectTemp /13-35 https://github.com/python/typeshed/blob/master/CONTRIBUTING.md +RedirectTemp /13-36 https://gist.github.com/asukakenji/ac8a05644a2e98f1d5ea8c299541fce9 +RedirectTemp /13-37 https://www.python.org/dev/peps/pep-0544/#runtime-checkable-decorator-and-narrowing-types-by-isinstance +RedirectTemp /13-38 https://www.python.org/dev/peps/pep-0544/#merging-and-extending-protocols +RedirectTemp /13-39 https://numpy.org/devdocs/user/basics.types.html +RedirectTemp /13-40 https://github.com/python/typeshed/blob/master/stdlib/statistics.pyi +RedirectTemp /13-41 https://bugs.python.org/issue41974 +RedirectTemp /13-42 https://glyph.twistedmatrix.com/2020/07/new-duck.html +RedirectTemp /13-43 https://glyph.twistedmatrix.com/2021/03/interfaces-and-protocols.html +RedirectTemp /13-44 https://plone.org/ +RedirectTemp /13-45 https://trypyramid.com/ +RedirectTemp /13-46 https://twistedmatrix.com/trac/ +RedirectTemp /13-47 https://www.artima.com/articles/contracts-in-python +RedirectTemp /13-48 https://martinfowler.com/bliki/DynamicTyping.html +RedirectTemp /13-49 https://martinfowler.com/bliki/RoleInterface.html +RedirectTemp /13-50 https://mypy.readthedocs.io/en/stable/protocols.html +RedirectTemp /13-51 https://pymotw.com/3/abc/index.html +RedirectTemp /13-52 https://www.python.org/dev/peps/pep-3119/ +RedirectTemp /13-53 https://www.python.org/dev/peps/pep-3141/ +RedirectTemp /13-54 https://docs.python.org/3/library/numbers.html +RedirectTemp /13-55 https://github.com/python/mypy/issues/3186 +RedirectTemp /13-56 https://stackoverflow.com/questions/69334475/how-to-hint-at-number-types-i-e-subclasses-of-number-not-numbers-themselv/69383462#69383462 +RedirectTemp /13-57 https://github.com/python/mypy/issues/3186 +RedirectTemp /13-58 https://martinfowler.com/articles/lean-inception/ +RedirectTemp /13-59 https://martinfowler.com +RedirectTemp /13-60 https://www.jetbrains.com/pycharm/ +RedirectTemp /13-61 https://wingware.com/ +RedirectTemp /13-62 https://code.visualstudio.com/ ############################################################ 14 RedirectTemp /14-1 http://worrydream.com/EarlyHistoryOfSmalltalk/ RedirectTemp /14-2 https://docs.python.org/3/tutorial/classes.html RedirectTemp /14-3 https://docs.python.org/3/library/collections.html#ordereddict-examples-and-recipes RedirectTemp /14-4 https://discuss.python.org/t/is-it-time-to-deprecate-unbound-super-methods/1833 -RedirectTemp /14-6 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types -RedirectTemp /14-7 https://docs.python.org/3/library/collections.html -RedirectTemp /14-9 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types -RedirectTemp /14-10 https://en.wikipedia.org/wiki/Breadth-first_search -RedirectTemp /14-11 https://www.python.org/download/releases/2.3/mro/ -RedirectTemp /14-12 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/uppermixin.py -RedirectTemp /14-13 https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html -RedirectTemp /14-14 https://docs.python.org/3/library/collections.abc.html -RedirectTemp /14-15 https://github.com/python/cpython/blob/8ece98a7e418c3c68a4c61bc47a2d0931b59a889/Lib/collections/__init__.py#L1084 -RedirectTemp /14-16 https://docs.python.org/3/library/http.server.html -RedirectTemp /14-17 https://docs.python.org/3/library/socketserver.html#socketserver.ForkingMixIn -RedirectTemp /14-18 https://docs.python.org/3/library/os.html#os.fork -RedirectTemp /14-19 https://en.wikipedia.org/wiki/POSIX -RedirectTemp /14-20 http://ccbv.co.uk/ -RedirectTemp /14-21 https://github.com/django/django/tree/main/django/views/generic -RedirectTemp /14-22 https://en.wikipedia.org/wiki/Template_method_pattern -RedirectTemp /14-23 https://docs.python.org/3/library/tkinter.html -RedirectTemp /14-24 https://docs.python.org/3/library/tkinter.ttk.html -RedirectTemp /14-25 https://docs.oracle.com/javase/10/docs/api/java/awt/package-tree.html -RedirectTemp /14-26 https://docs.oracle.com/javase/10/docs/api/javax/swing/package-tree.html -RedirectTemp /14-27 https://squeak.org/ -RedirectTemp /14-28 https://github.com/python/cpython/blob/8ed183391241f0c73e7ba7f42b1d49fc02985f7b/Lib/tkinter/__init__.py#L2618 -RedirectTemp /14-29 https://docs.python.org/3/library/socketserver.html -RedirectTemp /14-30 https://docs.python.org/3/library/socketserver.html#socketserver.BaseServer -RedirectTemp /14-32 https://docs.python.org/3/library/typing.html#typing.final -RedirectTemp /14-33 https://docs.python.org/3/library/typing.html#typing.Final -RedirectTemp /14-35 https://docs.python.org/3/library/collections.abc.html -RedirectTemp /14-36 https://hynek.me/articles/python-subclassing-redux/ -RedirectTemp /14-38 https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ -RedirectTemp /14-39 https://fuhm.net/super-harmful/ -RedirectTemp /14-40 https://www.artima.com/weblogs/viewpost.jsp?thread=246488 -RedirectTemp /14-41 https://www.artima.com/weblogs/viewpost.jsp?thread=281127 -RedirectTemp /14-42 https://www.artima.com/weblogs/viewpost.jsp?thread=246341 -RedirectTemp /14-43 https://www.artima.com/weblogs/viewpost.jsp?thread=246483 -RedirectTemp /14-44 https://www.artima.com/weblogs/viewpost.jsp?thread=236275 -RedirectTemp /14-45 https://www.artima.com/weblogs/viewpost.jsp?thread=236278 -RedirectTemp /14-46 https://www.artima.com/weblogs/viewpost.jsp?thread=237121 -RedirectTemp /14-47 https://python-patterns.guide/gang-of-four/composition-over-inheritance/ -RedirectTemp /14-48 https://python-patterns.guide/ -RedirectTemp /14-49 https://www.youtube.com/watch?v=3MNVP9-hglc -RedirectTemp /14-50 http://worrydream.com/EarlyHistoryOfSmalltalk/ -RedirectTemp /14-51 https://en.wikipedia.org/wiki/Polymorphism_(computer_science) +RedirectTemp /14-5 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /14-6 https://docs.python.org/3/library/collections.html +RedirectTemp /14-7 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/strkeydict_dictsub.py +RedirectTemp /14-8 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /14-9 https://en.wikipedia.org/wiki/Breadth-first_search +RedirectTemp /14-10 https://www.python.org/download/releases/2.3/mro/ +RedirectTemp /14-11 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/uppermixin.py +RedirectTemp /14-12 https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html +RedirectTemp /14-13 https://docs.python.org/3/library/collections.abc.html +RedirectTemp /14-14 https://github.com/python/cpython/blob/8ece98a7e418c3c68a4c61bc47a2d0931b59a889/Lib/collections/__init__.py#L1084 +RedirectTemp /14-15 https://docs.python.org/3/library/http.server.html +RedirectTemp /14-16 https://github.com/python/cpython/blob/17c23167942498296f0bdfffe52e72d53d66d693/Lib/http/server.py#L144 +RedirectTemp /14-17 https://github.com/python/cpython/blob/699ee016af5736ffc80f68359617611a22b72943/Lib/socketserver.py#L664 +RedirectTemp /14-18 https://docs.python.org/3/library/socketserver.html#socketserver.ForkingMixIn +RedirectTemp /14-19 https://docs.python.org/3/library/os.html#os.fork +RedirectTemp /14-20 https://en.wikipedia.org/wiki/POSIX +RedirectTemp /14-21 http://ccbv.co.uk/ +RedirectTemp /14-22 https://github.com/django/django/tree/main/django/views/generic +RedirectTemp /14-23 https://en.wikipedia.org/wiki/Template_method_pattern +RedirectTemp /14-24 https://docs.python.org/3/library/tkinter.html +RedirectTemp /14-25 https://docs.python.org/3/library/tkinter.ttk.html +RedirectTemp /14-26 https://docs.oracle.com/javase/10/docs/api/java/awt/package-tree.html +RedirectTemp /14-27 https://docs.oracle.com/javase/10/docs/api/javax/swing/package-tree.html +RedirectTemp /14-28 https://squeak.org/ +RedirectTemp /14-29 https://github.com/django/django/blob/b64db05b9cedd96905d637a2d824cbbf428e40e7/django/views/generic/list.py#L194 +RedirectTemp /14-30 https://github.com/python/cpython/blob/8ed183391241f0c73e7ba7f42b1d49fc02985f7b/Lib/tkinter/__init__.py#L2618 +RedirectTemp /14-31 https://docs.python.org/3/library/socketserver.html +RedirectTemp /14-32 https://docs.python.org/3/library/socketserver.html#socketserver.BaseServer +RedirectTemp /14-33 https://github.com/python/cpython/blob/699ee016af5736ffc80f68359617611a22b72943/Lib/socketserver.py#L153 +RedirectTemp /14-34 https://docs.python.org/3/library/typing.html#typing.final +RedirectTemp /14-35 https://docs.python.org/3/library/typing.html#typing.Final +RedirectTemp /14-36 https://docs.python.org/3/library/collections.abc.html +RedirectTemp /14-37 https://hynek.me/articles/python-subclassing-redux/ +RedirectTemp /14-38 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch08.html#super +RedirectTemp /14-39 https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ +RedirectTemp /14-40 https://fuhm.net/super-harmful/ +RedirectTemp /14-41 https://stackoverflow.com/questions/30190185/how-to-use-super-with-one-argument/30190341#30190341 +RedirectTemp /14-42 https://www.artima.com/weblogs/viewpost.jsp?thread=246488 +RedirectTemp /14-43 https://www.artima.com/weblogs/viewpost.jsp?thread=281127 +RedirectTemp /14-44 https://www.artima.com/weblogs/viewpost.jsp?thread=246341 +RedirectTemp /14-45 https://www.artima.com/weblogs/viewpost.jsp?thread=246483 +RedirectTemp /14-46 https://www.artima.com/weblogs/viewpost.jsp?thread=236275 +RedirectTemp /14-47 https://www.artima.com/weblogs/viewpost.jsp?thread=236278 +RedirectTemp /14-48 https://www.artima.com/weblogs/viewpost.jsp?thread=237121 +RedirectTemp /14-49 https://python-patterns.guide/gang-of-four/composition-over-inheritance/ +RedirectTemp /14-50 https://python-patterns.guide/ +RedirectTemp /14-51 https://www.youtube.com/watch?v=3MNVP9-hglc +RedirectTemp /14-52 http://worrydream.com/EarlyHistoryOfSmalltalk/ +RedirectTemp /14-53 https://en.wikipedia.org/wiki/Polymorphism_(computer_science) ############################################################ 15 RedirectTemp /15-1 https://www.youtube.com/watch?v=csL8DLXGNlU&t=92m5s +RedirectTemp /15-2 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/2and3/builtins.pyi#L1434 +RedirectTemp /15-3 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/2and3/builtins.pyi RedirectTemp /15-4 https://twitter.com/gwidion/status/1265384692464967680 RedirectTemp /15-5 https://pypi.org/project/pydantic/ +RedirectTemp /15-6 https://google.github.io/pytype/faq.html RedirectTemp /15-7 https://google.github.io/pytype/faq.html -RedirectTemp /15-8 https://google.github.io/pytype/faq.html -RedirectTemp /15-9 https://lxml.de/ -RedirectTemp /15-10 https://docs.python.org/3/library/xml.etree.elementtree.html -RedirectTemp /15-11 https://mypy.readthedocs.io/en/stable/common_issues.html -RedirectTemp /15-12 https://mypy.readthedocs.io/en/stable/common_issues.html#types-of-empty-collections -RedirectTemp /15-13 https://github.com/python/typing/issues/182 -RedirectTemp /15-14 https://pypi.org/project/pydantic/ -RedirectTemp /15-16 https://mypy.readthedocs.io/en/stable/type_narrowing.html#casts -RedirectTemp /15-17 https://www.python.org/dev/peps/pep-0484/#casts -RedirectTemp /15-18 https://github.com/python/typeshed/issues/5535 -RedirectTemp /15-19 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams +RedirectTemp /15-8 https://lxml.de/ +RedirectTemp /15-9 https://docs.python.org/3/library/xml.etree.elementtree.html +RedirectTemp /15-10 https://mypy.readthedocs.io/en/stable/common_issues.html +RedirectTemp /15-11 https://mypy.readthedocs.io/en/stable/common_issues.html#types-of-empty-collections +RedirectTemp /15-12 https://github.com/python/typing/issues/182 +RedirectTemp /15-13 https://pypi.org/project/pydantic/ +RedirectTemp /15-14 https://mypy.readthedocs.io/en/stable/type_narrowing.html#casts +RedirectTemp /15-15 https://github.com/python/cpython/blob/bee66d3cb98e740f9d8057eb7f503122052ca5d8/Lib/typing.py#L1340 +RedirectTemp /15-16 https://www.python.org/dev/peps/pep-0484/#casts +RedirectTemp /15-17 https://github.com/python/typeshed/issues/5535 +RedirectTemp /15-18 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams +RedirectTemp /15-19 https://github.com/python/cpython/blob/b798ab06937f8bb24b444a49dd42e11fff15e654/Lib/test/test_asyncio/test_server.py#L55 RedirectTemp /15-20 https://en.wikipedia.org/wiki/Code_smell -RedirectTemp /15-21 https://mypy.readthedocs.io/en/stable/error_codes.html#error-codes -RedirectTemp /15-22 https://github.com/fluentpython/example-code-2e/blob/master/15-more-types/clip_annot.py +RedirectTemp /15-21 https://mail.python.org/archives/list/typing-sig@python.org/message/5LCWMN2UY2UQNLC5Z47GHBZKSPZW4I63/ +RedirectTemp /15-22 https://mypy.readthedocs.io/en/stable/error_codes.html#error-codes +RedirectTemp /15-23 https://github.com/fluentpython/example-code-2e/blob/master/15-more-types/clip_annot.py RedirectTemp /15-24 https://docs.python.org/3/library/typing.html#introspection-helpers RedirectTemp /15-25 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations -RedirectTemp /15-27 https://www.python.org/dev/peps/pep-0563/#abstract -RedirectTemp /15-29 https://docs.python.org/3.10/howto/annotations.html -RedirectTemp /15-32 https://docs.python.org/3/library/typing.html#user-defined-generic-types -RedirectTemp /15-33 https://docs.python.org/3.10/library/typing.html#typing.FrozenSet -RedirectTemp /15-34 https://docs.python.org/3.10/library/typing.html#typing.Generator -RedirectTemp /15-36 https://docs.python.org/3.10/library/typing.html#typing.AsyncGenerator -RedirectTemp /15-62 https://www.oreilly.com/library/view/robust-python/9781098100650/ -RedirectTemp /15-63 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance -RedirectTemp /15-64 https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generic-types -RedirectTemp /15-65 https://mypy.readthedocs.io/en/stable/common_issues.html#variance -RedirectTemp /15-67 https://www.artima.com/weblogs/viewpost.jsp?thread=85551 -RedirectTemp /15-68 https://dl.acm.org/action/cookieAbsent -RedirectTemp /15-69 http://bracha.org/pluggableTypesPosition.pdf -RedirectTemp /15-70 https://www.atomickotlin.com/atomickotlin/ -RedirectTemp /15-71 https://www.informit.com/store/effective-java-9780134685991 -RedirectTemp /15-72 https://www.manning.com/books/programming-with-types -RedirectTemp /15-73 https://www.oreilly.com/library/view/programming-typescript/9781492037644/ -RedirectTemp /15-74 https://www.informit.com/store/dart-programming-language-9780321927705 -RedirectTemp /15-75 https://www.yodaiken.com/2017/09/15/bad-ideas-in-type-theory/ -RedirectTemp /15-76 https://www.yodaiken.com/2017/11/30/types-considered-harmful-ii/ -RedirectTemp /15-77 https://web.archive.org/web/20071010002142/http://weblogs.java.net/blog/arnold/archive/2005/06/generics_consid_1.html -RedirectTemp /15-78 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance +RedirectTemp /15-26 https://www.python.org/dev/peps/pep-0563/#abstract +RedirectTemp /15-27 https://mail.python.org/archives/list/python-dev@python.org/message/ZBJ7MD6CSGM6LZAOTET7GXAVBZB7O77O/ +RedirectTemp /15-28 https://docs.python.org/3.10/howto/annotations.html +RedirectTemp /15-29 https://docs.python.org/3/library/typing.html#user-defined-generic-types +RedirectTemp /15-30 https://github.com/python/typeshed/blob/bfc83c365a0b26ab16586beac77ff16729d0e473/stdlib/builtins.pyi#L743 +RedirectTemp /15-31 https://docs.python.org/3.10/library/typing.html#typing.FrozenSet +RedirectTemp /15-32 https://docs.python.org/3.10/library/typing.html#typing.Generator +RedirectTemp /15-33 https://docs.python.org/3.10/library/typing.html#typing.AsyncGenerator +RedirectTemp /15-34 https://github.com/python/cpython/blob/46b16d0bdbb1722daed10389e27226a2370f1635/Lib/typing.py#L1786 +RedirectTemp /15-35 https://github.com/python/typeshed/blob/2a9f081abbf01134e4e04ced6a750107db904d70/stdlib/builtins.pyi#L239 +RedirectTemp /15-36 https://www.oreilly.com/library/view/robust-python/9781098100650/ +RedirectTemp /15-37 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance +RedirectTemp /15-38 https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generic-types +RedirectTemp /15-39 https://mypy.readthedocs.io/en/stable/common_issues.html#variance +RedirectTemp /15-40 https://www.artima.com/weblogs/viewpost.jsp?thread=85551 +RedirectTemp /15-41 https://dl.acm.org/action/cookieAbsent +RedirectTemp /15-42 http://bracha.org/pluggableTypesPosition.pdf +RedirectTemp /15-43 https://www.researchgate.net/publication/213886116_Static_Typing_Where_Possible_Dynamic_Typing_When_Needed_The_End_of_the_Cold_War_Between_Programming_Languages +RedirectTemp /15-44 https://www.atomickotlin.com/atomickotlin/ +RedirectTemp /15-45 https://www.informit.com/store/effective-java-9780134685991 +RedirectTemp /15-46 https://www.manning.com/books/programming-with-types +RedirectTemp /15-47 https://www.oreilly.com/library/view/programming-typescript/9781492037644/ +RedirectTemp /15-48 https://www.informit.com/store/dart-programming-language-9780321927705 +RedirectTemp /15-49 https://www.yodaiken.com/2017/09/15/bad-ideas-in-type-theory/ +RedirectTemp /15-50 https://www.yodaiken.com/2017/11/30/types-considered-harmful-ii/ +RedirectTemp /15-51 https://web.archive.org/web/20071010002142/http://weblogs.java.net/blog/arnold/archive/2005/06/generics_consid_1.html +RedirectTemp /15-52 https://github.com/python/cpython/blob/3e7ee02327db13e4337374597cdc4458ecb9e3ad/Lib/asyncio/trsock.py#L5 +RedirectTemp /15-53 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance ############################################################ 16 RedirectTemp /16-1 http://www.gotw.ca/publications/c_family_interview.htm RedirectTemp /16-2 https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations +RedirectTemp /16-3 https://docs.python.org/3/reference/datamodel.html#object.__neg__ RedirectTemp /16-4 https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#boolean-indexing RedirectTemp /16-5 https://docs.python.org/3/library/collections.html#collections.Counter RedirectTemp /16-6 https://docs.python.org/3/reference/datamodel.html#emulating-container-types -RedirectTemp /16-8 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations -RedirectTemp /16-10 https://www.fluentpython.com/lingo/#fail-fast -RedirectTemp /16-11 https://neopythonic.blogspot.com/2019/03/why-operators-are-useful.html -RedirectTemp /16-12 https://treyhunner.com/2019/03/python-deep-comparisons-and-code-readability/ -RedirectTemp /16-14 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations -RedirectTemp /16-15 https://docs.python.org/3/library/pathlib.html -RedirectTemp /16-16 https://pypi.org/project/scapy/ -RedirectTemp /16-17 https://scapy.readthedocs.io/en/latest/usage.html#stacking-layers -RedirectTemp /16-18 https://docs.python.org/3/library/functools.html#functools.total_ordering -RedirectTemp /16-19 https://wiki.illinois.edu//wiki/download/attachments/273416327/ingalls.pdf -RedirectTemp /16-20 https://wiki.illinois.edu//wiki/download/attachments/273416327/double-dispatch.pdf -RedirectTemp /16-21 http://www.gotw.ca/publications/c_family_interview.htm -RedirectTemp /16-22 https://doc.rust-lang.org/std/ops/index.html -RedirectTemp /16-23 https://www.fluentpython.com/lingo/#lazy +RedirectTemp /16-7 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations +RedirectTemp /16-8 https://www.fluentpython.com/lingo/#fail-fast +RedirectTemp /16-9 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Objects/typeobject.c#L4598 +RedirectTemp /16-10 https://neopythonic.blogspot.com/2019/03/why-operators-are-useful.html +RedirectTemp /16-11 https://treyhunner.com/2019/03/python-deep-comparisons-and-code-readability/ +RedirectTemp /16-12 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations +RedirectTemp /16-13 https://docs.python.org/3/library/pathlib.html +RedirectTemp /16-14 https://pypi.org/project/scapy/ +RedirectTemp /16-15 https://scapy.readthedocs.io/en/latest/usage.html#stacking-layers +RedirectTemp /16-16 https://docs.python.org/3/library/functools.html#functools.total_ordering +RedirectTemp /16-17 https://wiki.illinois.edu//wiki/download/attachments/273416327/ingalls.pdf +RedirectTemp /16-18 https://wiki.illinois.edu//wiki/download/attachments/273416327/double-dispatch.pdf +RedirectTemp /16-19 http://www.gotw.ca/publications/c_family_interview.htm +RedirectTemp /16-20 http://www.gotw.ca/publications/c_family_interview.htm +RedirectTemp /16-21 https://doc.rust-lang.org/std/ops/index.html +RedirectTemp /16-22 https://www.fluentpython.com/lingo/#lazy ############################################################ 17 RedirectTemp /17-1 http://www.paulgraham.com/icad.html -RedirectTemp /17-5 https://en.wikipedia.org/wiki/Sentinel_value -RedirectTemp /17-6 https://docs.python.org/3.10/library/functions.html#iter -RedirectTemp /17-7 https://docs.python.org/3.10/library/functions.html#iter -RedirectTemp /17-8 https://github.com/python/cpython/blob/b1930bf75f276cd7ca08c4455298128d89adf7d1/Lib/_collections_abc.py#L271 -RedirectTemp /17-9 https://github.com/python/cpython/blob/main/Lib/types.py#L6 -RedirectTemp /17-10 https://en.wikipedia.org/wiki/CLU_(programming_language) -RedirectTemp /17-12 https://docs.python.org/3/glossary.html -RedirectTemp /17-13 https://docs.python.org/3/glossary.html#term-generator-iterator -RedirectTemp /17-14 https://docs.python.org/3/glossary.html#term-generator-expression -RedirectTemp /17-15 https://marc.info/?l=python-list&m=141826925106951&w=2 -RedirectTemp /17-17 https://docs.python.org/3/library/itertools.html -RedirectTemp /17-18 https://docs.python.org/3/library/exceptions.html#exception-hierarchy -RedirectTemp /17-19 https://en.wikipedia.org/wiki/Depth-first_search -RedirectTemp /17-20 https://docs.python.org/3.10/library/typing.html#typing.TypeAlias -RedirectTemp /17-22 https://docs.python.org/3/library/typing.html#typing.Generator -RedirectTemp /17-25 http://www.dabeaz.com/coroutines/Coroutines.pdf -RedirectTemp /17-26 http://www.dabeaz.com/coroutines/Coroutines.pdf -RedirectTemp /17-27 https://mail.python.org/pipermail/python-ideas/2009-April/003841.html -RedirectTemp /17-28 https://mail.python.org/pipermail/python-ideas/2009-April/003912.html -RedirectTemp /17-31 https://docs.python.org/3/library/exceptions.html#StopIteration -RedirectTemp /17-32 https://docs.python.org/3/reference/expressions.html#yield-expressions -RedirectTemp /17-33 https://docs.python.org/3/reference/index.html -RedirectTemp /17-36 http://catb.org/~esr/jargon/html/G/grok.html -RedirectTemp /17-37 https://docs.python.org/3/reference/expressions.html#yieldexpr -RedirectTemp /17-39 https://docs.python.org/3/library/itertools.html#itertools-recipes -RedirectTemp /17-40 https://more-itertools.readthedocs.io/en/stable/index.html -RedirectTemp /17-41 https://rittau.org/2006/11/java-iterators-are-not-iterable/ -RedirectTemp /17-42 https://docs.python.org/3/whatsnew/3.3.html#pep-380-syntax-for-delegating-to-a-subgenerator -RedirectTemp /17-45 http://www.dabeaz.com/generators/ -RedirectTemp /17-46 http://www.dabeaz.com/coroutines/ -RedirectTemp /17-47 https://archive.org/details/pyvideo_213___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-1-of-3 -RedirectTemp /17-48 https://archive.org/details/pyvideo_215___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-2-of-3 -RedirectTemp /17-49 https://archive.org/details/pyvideo_214___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-3-of-3 -RedirectTemp /17-50 http://www.dabeaz.com/finalgenerator/ -RedirectTemp /17-51 https://web.archive.org/web/20200218150637/http://seriously.dontusethiscode.com/2013/05/01/greedy-coroutine.html -RedirectTemp /17-52 https://effectivepython.com/ -RedirectTemp /17-53 https://effectivepython.com/2015/03/10/consider-coroutines-to-run-many-functions-concurrently -RedirectTemp /17-54 https://en.wikipedia.org/wiki/Conway's_Game_of_Life -RedirectTemp /17-55 https://gist.github.com/ramalho/da5590bc38c973408839 -RedirectTemp /17-56 https://gist.github.com/ramalho/da5590bc38c973408839 -RedirectTemp /17-57 https://journal.code4lib.org/articles/4893 -RedirectTemp /17-58 https://github.com/fluentpython/isis2json -RedirectTemp /17-59 https://github.com/fluentpython/isis2json/blob/master/README.rst +RedirectTemp /17-2 https://en.wikipedia.org/wiki/Sentinel_value +RedirectTemp /17-3 https://docs.python.org/3.10/library/functions.html#iter +RedirectTemp /17-4 https://docs.python.org/3.10/library/functions.html#iter +RedirectTemp /17-5 https://github.com/python/cpython/blob/b1930bf75f276cd7ca08c4455298128d89adf7d1/Lib/_collections_abc.py#L271 +RedirectTemp /17-6 https://github.com/python/cpython/blob/main/Lib/types.py#L6 +RedirectTemp /17-7 https://en.wikipedia.org/wiki/CLU_(programming_language) +RedirectTemp /17-8 https://docs.python.org/3/glossary.html +RedirectTemp /17-9 https://docs.python.org/3/glossary.html#term-generator-iterator +RedirectTemp /17-10 https://docs.python.org/3/glossary.html#term-generator-expression +RedirectTemp /17-11 https://marc.info/?l=python-list&m=141826925106951&w=2 +RedirectTemp /17-12 https://docs.python.org/3/library/os.html#os.walk +RedirectTemp /17-13 https://docs.python.org/3/library/itertools.html +RedirectTemp /17-14 https://docs.python.org/3/library/exceptions.html#exception-hierarchy +RedirectTemp /17-15 https://en.wikipedia.org/wiki/Depth-first_search +RedirectTemp /17-16 https://docs.python.org/3.10/library/typing.html#typing.TypeAlias +RedirectTemp /17-17 https://docs.python.org/3/library/typing.html#typing.Generator +RedirectTemp /17-18 http://www.dabeaz.com/coroutines/Coroutines.pdf +RedirectTemp /17-19 http://www.dabeaz.com/coroutines/Coroutines.pdf +RedirectTemp /17-20 https://mail.python.org/pipermail/python-ideas/2009-April/003841.html +RedirectTemp /17-21 https://mail.python.org/pipermail/python-ideas/2009-April/003912.html +RedirectTemp /17-22 https://docs.python.org/3/library/exceptions.html#StopIteration +RedirectTemp /17-23 https://docs.python.org/3/reference/expressions.html#yield-expressions +RedirectTemp /17-24 https://docs.python.org/3/reference/index.html +RedirectTemp /17-25 https://github.com/python/cpython/blob/6f743e7a4da904f61dfa84cc7d7385e4dcc79ac5/Lib/typing.py#L2060 +RedirectTemp /17-26 http://catb.org/~esr/jargon/html/G/grok.html +RedirectTemp /17-27 https://docs.python.org/3/reference/expressions.html#yieldexpr +RedirectTemp /17-28 https://docs.python.org/3/library/itertools.html +RedirectTemp /17-29 https://docs.python.org/3/library/itertools.html#itertools-recipes +RedirectTemp /17-30 https://more-itertools.readthedocs.io/en/stable/index.html +RedirectTemp /17-31 https://rittau.org/2006/11/java-iterators-are-not-iterable/ +RedirectTemp /17-32 https://docs.python.org/3/whatsnew/3.3.html#pep-380-syntax-for-delegating-to-a-subgenerator +RedirectTemp /17-33 http://www.dabeaz.com/generators/ +RedirectTemp /17-34 http://www.dabeaz.com/coroutines/ +RedirectTemp /17-35 https://archive.org/details/pyvideo_213___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-1-of-3 +RedirectTemp /17-36 https://archive.org/details/pyvideo_215___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-2-of-3 +RedirectTemp /17-37 https://archive.org/details/pyvideo_214___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-3-of-3 +RedirectTemp /17-38 http://www.dabeaz.com/finalgenerator/ +RedirectTemp /17-39 https://web.archive.org/web/20200218150637/http://seriously.dontusethiscode.com/2013/05/01/greedy-coroutine.html +RedirectTemp /17-40 https://effectivepython.com/ +RedirectTemp /17-41 https://effectivepython.com/2015/03/10/consider-coroutines-to-run-many-functions-concurrently +RedirectTemp /17-42 https://en.wikipedia.org/wiki/Conway's_Game_of_Life +RedirectTemp /17-43 https://gist.github.com/ramalho/da5590bc38c973408839 +RedirectTemp /17-44 https://gist.github.com/ramalho/da5590bc38c973408839 +RedirectTemp /17-45 https://journal.code4lib.org/articles/4893 +RedirectTemp /17-46 https://github.com/fluentpython/isis2json +RedirectTemp /17-47 https://github.com/fluentpython/isis2json/blob/master/README.rst ############################################################ 18 RedirectTemp /18-1 https://pyvideo.org/video/1669/keynote-3/ RedirectTemp /18-2 https://docs.python.org/3/library/sqlite3.html#using-the-connection-as-a-context-manager @@ -678,42 +726,53 @@ RedirectTemp /18-4 https://docs.python.org/3/library/decimal.html#decimal.localc RedirectTemp /18-5 https://docs.python.org/3/library/unittest.mock.html#patch RedirectTemp /18-6 https://docs.python.org/3/library/contextlib.html#contextlib.redirect_stdout RedirectTemp /18-7 https://docs.python.org/3/library/sys.html#sys.exc_info -RedirectTemp /18-9 https://en.wikipedia.org/wiki/LL_parser -RedirectTemp /18-10 https://docs.python.org/3/library/contextlib.html +RedirectTemp /18-8 https://en.wikipedia.org/wiki/LL_parser +RedirectTemp /18-9 https://docs.python.org/3/library/contextlib.html +RedirectTemp /18-10 https://github.com/python/cpython/blob/8afab2ebbc1b343cd88d058914cf622fe687a2be/Lib/contextlib.py#L123 RedirectTemp /18-11 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ RedirectTemp /18-12 https://docs.python.org/3/library/fileinput.html#fileinput.input RedirectTemp /18-13 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ RedirectTemp /18-14 https://en.wikipedia.org/wiki/Euclidean_algorithm RedirectTemp /18-15 https://github.com/fluentpython/example-code-2e/tree/master/18-with-match/lispy/py3.10/ -RedirectTemp /18-17 https://github.com/python/typeshed/issues/6042 -RedirectTemp /18-18 https://github.com/fluentpython/lispy/tree/main/mylis -RedirectTemp /18-19 https://mitpress.mit.edu/sites/default/files/sicp/index.html -RedirectTemp /18-20 https://www.python.org/dev/peps/pep-0634/#or-patterns -RedirectTemp /18-21 https://en.wikipedia.org/wiki/Lambda#Character_encodings -RedirectTemp /18-22 https://docs.python.org/3/reference/compound_stmts.html -RedirectTemp /18-23 https://docs.python.org/3/glossary.html#term-eafp -RedirectTemp /18-24 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 -RedirectTemp /18-25 https://docs.python.org/3/reference/compound_stmts.html -RedirectTemp /18-26 https://stackoverflow.com/questions/16138232/is-it-a-good-practice-to-use-try-except-else-in-python -RedirectTemp /18-27 https://docs.python.org/3/library/stdtypes.html#typecontextmanager -RedirectTemp /18-28 https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers -RedirectTemp /18-30 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 -RedirectTemp /18-31 https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger-1?slide=34 -RedirectTemp /18-32 https://preshing.com/20110920/the-python-with-statement-by-example/ -RedirectTemp /18-33 https://www.rath.org/on-the-beauty-of-pythons-exitstack.html -RedirectTemp /18-34 https://github.com/norvig/pytudes -RedirectTemp /18-35 https://github.com/fluentpython/lispy -RedirectTemp /18-36 https://racket-lang.org/ -RedirectTemp /18-37 https://pyvideo.org/video/1669/keynote-3/ -RedirectTemp /18-38 https://en.wikipedia.org/wiki/Tail_call -RedirectTemp /18-39 https://2ality.com/2015/06/tail-call-optimization.html -RedirectTemp /18-40 http://neopythonic.blogspot.com/2009/04/final-words-on-tail-calls.html -RedirectTemp /18-41 https://webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit/ -RedirectTemp /18-42 http://kangax.github.io/compat-table/es6/ -RedirectTemp /18-43 https://world.hey.com/mgmarlow/what-happened-to-proper-tail-calls-in-javascript-5494c256 -RedirectTemp /18-44 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html -RedirectTemp /18-45 https://github.com/fluentpython/lispy/blob/main/mylis/mylis_2/lis.py -RedirectTemp /18-46 https://github.com/fluentpython/lispy/tree/main/mylis +RedirectTemp /18-16 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lispy.py +RedirectTemp /18-17 https://github.com/fluentpython/example-code-2e/blob/6527037ae7319ba370a1ee2d9fe79214d0ed9452/18-with-match/lispy/py3.10/lis.py#L35 +RedirectTemp /18-18 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/examples_test.py +RedirectTemp /18-19 https://github.com/python/typeshed/issues/6042 +RedirectTemp /18-20 https://github.com/fluentpython/lispy/tree/main/mylis +RedirectTemp /18-21 https://github.com/fluentpython/example-code-2e/blob/00e4741926e1b771ee7c753148b1415c0bd12e39/02-array-seq/lispy/py3.10/examples_test.py +RedirectTemp /18-22 https://mitpress.mit.edu/sites/default/files/sicp/index.html +RedirectTemp /18-23 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/examples_test.py +RedirectTemp /18-24 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/lis.py +RedirectTemp /18-25 https://www.python.org/dev/peps/pep-0634/#or-patterns +RedirectTemp /18-26 https://en.wikipedia.org/wiki/Lambda#Character_encodings +RedirectTemp /18-27 https://docs.python.org/3/reference/compound_stmts.html +RedirectTemp /18-28 https://docs.python.org/3/glossary.html#term-eafp +RedirectTemp /18-29 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 +RedirectTemp /18-30 https://docs.python.org/3/reference/compound_stmts.html +RedirectTemp /18-31 https://stackoverflow.com/questions/16138232/is-it-a-good-practice-to-use-try-except-else-in-python +RedirectTemp /18-32 https://docs.python.org/3/library/stdtypes.html#typecontextmanager +RedirectTemp /18-33 https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers +RedirectTemp /18-34 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 +RedirectTemp /18-35 https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger-1?slide=34 +RedirectTemp /18-36 https://preshing.com/20110920/the-python-with-statement-by-example/ +RedirectTemp /18-37 https://www.rath.org/on-the-beauty-of-pythons-exitstack.html +RedirectTemp /18-38 https://norvig.com/lispy.html +RedirectTemp /18-39 https://norvig.com/lispy2.html +RedirectTemp /18-40 https://github.com/norvig/pytudes +RedirectTemp /18-41 https://github.com/fluentpython/lispy +RedirectTemp /18-42 https://racket-lang.org/ +RedirectTemp /18-43 https://pyvideo.org/video/1669/keynote-3/ +RedirectTemp /18-44 https://en.wikipedia.org/wiki/Tail_call +RedirectTemp /18-45 https://2ality.com/2015/06/tail-call-optimization.html +RedirectTemp /18-46 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lis.py +RedirectTemp /18-47 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lispy.py +RedirectTemp /18-48 http://neopythonic.blogspot.com/2009/04/final-words-on-tail-calls.html +RedirectTemp /18-49 https://webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit/ +RedirectTemp /18-50 http://kangax.github.io/compat-table/es6/ +RedirectTemp /18-51 https://world.hey.com/mgmarlow/what-happened-to-proper-tail-calls-in-javascript-5494c256 +RedirectTemp /18-52 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html +RedirectTemp /18-53 https://github.com/fluentpython/lispy/blob/main/mylis/mylis_2/lis.py +RedirectTemp /18-54 https://github.com/fluentpython/lispy/tree/main/mylis ############################################################ 19 RedirectTemp /19-1 https://go.dev/blog/waza-talk RedirectTemp /19-2 https://en.wikipedia.org/wiki/Graphics_processing_unit @@ -721,69 +780,81 @@ RedirectTemp /19-3 https://docs.python.org/3/library/sys.html#sys.getswitchinter RedirectTemp /19-4 https://docs.python.org/3/library/sys.html#sys.setswitchinterval RedirectTemp /19-5 https://en.wikipedia.org/wiki/System_call RedirectTemp /19-6 https://mail.python.org/pipermail/python-dev/2009-October/093356.html -RedirectTemp /19-8 http://www.dabeaz.com/finalgenerator/ -RedirectTemp /19-9 https://docs.python.org/3/library/threading.html#thread-objects -RedirectTemp /19-10 https://www.pypy.org/ -RedirectTemp /19-11 https://mail.python.org/pipermail/python-list/2009-February/675659.html -RedirectTemp /19-12 https://en.wikipedia.org/wiki/Braille_Patterns -RedirectTemp /19-13 https://docs.python.org/3/library/multiprocessing.shared_memory.html +RedirectTemp /19-7 http://www.dabeaz.com/finalgenerator/ +RedirectTemp /19-8 https://docs.python.org/3/library/threading.html#thread-objects +RedirectTemp /19-9 https://www.pypy.org/ +RedirectTemp /19-10 https://mail.python.org/pipermail/python-list/2009-February/675659.html +RedirectTemp /19-11 https://en.wikipedia.org/wiki/Braille_Patterns +RedirectTemp /19-12 https://docs.python.org/3/library/multiprocessing.shared_memory.html +RedirectTemp /19-13 https://docs.python.org/3/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.ShareableList RedirectTemp /19-14 https://greenlet.readthedocs.io/en/latest/ -RedirectTemp /19-15 https://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html -RedirectTemp /19-16 http://www.gevent.org/ -RedirectTemp /19-17 https://github.com/gevent/gevent/wiki/Projects +RedirectTemp /19-15 https://docs.sqlalchemy.org/en/14/changelog/migration_14.html#asynchronous-io-support-for-core-and-orm +RedirectTemp /19-16 https://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html +RedirectTemp /19-17 http://www.gevent.org/ +RedirectTemp /19-18 https://github.com/gevent/gevent/wiki/Projects RedirectTemp /19-19 https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor-example RedirectTemp /19-20 https://github.com/python/asyncio/issues/284 RedirectTemp /19-21 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor +RedirectTemp /19-22 https://mail.python.org/archives/list/python-dev@python.org/message/JBYXQH3NV3YBF7P2HLHB5CD6V3GVTY55/ RedirectTemp /19-23 https://docs.python.org/3/library/queue.html#queue.SimpleQueue.get RedirectTemp /19-24 https://en.wikipedia.org/wiki/Race_condition -RedirectTemp /19-26 https://en.wikipedia.org/wiki/Context_switch -RedirectTemp /19-27 http://www.gotw.ca/publications/concurrency-ddj.htm -RedirectTemp /19-28 https://www.ansible.com/ -RedirectTemp /19-29 https://saltproject.io/ -RedirectTemp /19-30 https://www.fabfile.org/ -RedirectTemp /19-31 https://jupyter.org/ -RedirectTemp /19-32 https://docs.bokeh.org/en/latest/index.html -RedirectTemp /19-33 https://www.tensorflow.org/ -RedirectTemp /19-34 https://pytorch.org/ -RedirectTemp /19-35 https://www.oreilly.com/radar/where-programming-ops-ai-and-the-cloud-are-headed-in-2021/ -RedirectTemp /19-38 https://www.youtube.com/watch?v=ods97a5Pzw0 -RedirectTemp /19-39 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy +RedirectTemp /19-25 https://github.com/fluentpython/example-code-2e/commit/2c1230579db99738a5e5e6802063bda585f6476d +RedirectTemp /19-26 https://github.com/fluentpython/example-code-2e/blob/master/19-concurrency/primes/README.md +RedirectTemp /19-27 https://github.com/fluentpython/example-code-2e/blob/master/19-concurrency/primes/threads.py +RedirectTemp /19-28 https://en.wikipedia.org/wiki/Context_switch +RedirectTemp /19-29 http://www.gotw.ca/publications/concurrency-ddj.htm +RedirectTemp /19-30 https://www.ansible.com/ +RedirectTemp /19-31 https://saltproject.io/ +RedirectTemp /19-32 https://www.fabfile.org/ +RedirectTemp /19-33 https://engineering.fb.com/2016/05/27/production-engineering/python-in-production-engineering/ +RedirectTemp /19-34 https://jupyter.org/ +RedirectTemp /19-35 https://docs.bokeh.org/en/latest/index.html +RedirectTemp /19-36 https://www.tensorflow.org/ +RedirectTemp /19-37 https://pytorch.org/ +RedirectTemp /19-38 https://www.oreilly.com/radar/where-programming-ops-ai-and-the-cloud-are-headed-in-2021/ +RedirectTemp /19-39 https://www.youtube.com/watch?v=ods97a5Pzw0 +RedirectTemp /19-40 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy RedirectTemp /19-41 https://modwsgi.readthedocs.io/en/master/ RedirectTemp /19-42 https://uwsgi-docs.readthedocs.io/en/latest/ -RedirectTemp /19-44 https://unit.nginx.org/ -RedirectTemp /19-45 https://www.techatbloomberg.com/blog/configuring-uwsgi-production-deployment/ -RedirectTemp /19-46 https://www.youtube.com/watch?v=p6R1h2Nn468 -RedirectTemp /19-47 https://asgi.readthedocs.io/en/latest/index.html -RedirectTemp /19-48 https://docs.celeryproject.org/en/stable/getting-started/introduction.html -RedirectTemp /19-49 https://python-rq.org/ +RedirectTemp /19-43 https://unit.nginx.org/ +RedirectTemp /19-44 https://www.techatbloomberg.com/blog/configuring-uwsgi-production-deployment/ +RedirectTemp /19-45 https://www.youtube.com/watch?v=p6R1h2Nn468 +RedirectTemp /19-46 https://asgi.readthedocs.io/en/latest/index.html +RedirectTemp /19-47 https://docs.celeryproject.org/en/stable/getting-started/introduction.html +RedirectTemp /19-48 https://python-rq.org/ +RedirectTemp /19-49 https://docs.celeryproject.org/en/stable/faq.html#what-kinds-of-things-should-i-use-celery-for RedirectTemp /19-50 https://redis.io/ RedirectTemp /19-51 https://realpython.com/intro-to-python-threading/ RedirectTemp /19-52 https://pymotw.com/3/concurrency.html +RedirectTemp /19-53 https://www.pearson.com/us/higher-education/program/Hellmann-Python-3-Standard-Library-by-Example-The/PGM328871.html RedirectTemp /19-54 https://docs.python.org/3/library/multiprocessing.html#programming-guidelines -RedirectTemp /19-56 https://docs.python.org/3/library/multiprocessing.html -RedirectTemp /19-57 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ -RedirectTemp /19-58 https://link.springer.com/book/10.1007/978-1-4842-5793-7?error=cookies_not_supported&code=2ed5d61d-ae9f-4f3d-94ac-0f68cf45ea4f -RedirectTemp /19-59 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 -RedirectTemp /19-61 https://greenteapress.com/wp/semaphores/ -RedirectTemp /19-62 https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock -RedirectTemp /19-63 https://www.artima.com/weblogs/viewpost.jsp?thread=214235 -RedirectTemp /19-64 http://jessenoller.com/blog/2009/02/01/python-threads-and-the-global-interpreter-lock -RedirectTemp /19-65 https://realpython.com/products/cpython-internals-book/ -RedirectTemp /19-66 http://www.dabeaz.com/GIL/ -RedirectTemp /19-67 http://www.dabeaz.com/python/UnderstandingGIL.pdf -RedirectTemp /19-68 https://bugs.python.org/issue7946#msg223110 -RedirectTemp /19-69 https://bugs.python.org/issue7946 -RedirectTemp /19-70 https://www.fullstackpython.com/ -RedirectTemp /19-71 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ -RedirectTemp /19-72 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 -RedirectTemp /19-73 https://www.packtpub.com/product/distributed-computing-with-python/9781785889691 -RedirectTemp /19-74 https://towardsdatascience.com/python-performance-and-gpus-1be860ffd58d?gi=6a57a172ab5e +RedirectTemp /19-55 https://docs.python.org/3/library/multiprocessing.html +RedirectTemp /19-56 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ +RedirectTemp /19-57 https://link.springer.com/book/10.1007/978-1-4842-5793-7?error=cookies_not_supported&code=2ed5d61d-ae9f-4f3d-94ac-0f68cf45ea4f +RedirectTemp /19-58 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 +RedirectTemp /19-59 https://greenteapress.com/wp/semaphores/ +RedirectTemp /19-60 https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock +RedirectTemp /19-61 https://docs.python.org/3/faq/library.html#can-t-we-get-rid-of-the-global-interpreter-lock +RedirectTemp /19-62 https://www.artima.com/weblogs/viewpost.jsp?thread=214235 +RedirectTemp /19-63 http://jessenoller.com/blog/2009/02/01/python-threads-and-the-global-interpreter-lock +RedirectTemp /19-64 https://realpython.com/products/cpython-internals-book/ +RedirectTemp /19-65 http://www.dabeaz.com/GIL/ +RedirectTemp /19-66 http://www.dabeaz.com/python/UnderstandingGIL.pdf +RedirectTemp /19-67 https://bugs.python.org/issue7946#msg223110 +RedirectTemp /19-68 https://bugs.python.org/issue7946 +RedirectTemp /19-69 https://www.fullstackpython.com/ +RedirectTemp /19-70 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ +RedirectTemp /19-71 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 +RedirectTemp /19-72 https://www.packtpub.com/product/distributed-computing-with-python/9781785889691 +RedirectTemp /19-73 https://towardsdatascience.com/python-performance-and-gpus-1be860ffd58d?gi=a7d537cc2fb4 +RedirectTemp /19-74 https://instagram-engineering.com/web-service-efficiency-at-instagram-with-python-4976d078e366?gi=12a441991c88 RedirectTemp /19-75 https://www.oreilly.com/library/view/architecture-patterns-with/9781492052197/ RedirectTemp /19-76 https://www.cosmicpython.com/ RedirectTemp /19-77 https://pypi.org/project/lelo/ RedirectTemp /19-78 https://github.com/npryce/python-parallelize RedirectTemp /19-79 https://github.com/ericsnowcurrently/multi-core-python/wiki -RedirectTemp /19-81 https://gist.github.com/markshannon/79cace3656b40e21b7021504daee950c +RedirectTemp /19-80 https://gist.github.com/markshannon/79cace3656b40e21b7021504daee950c +RedirectTemp /19-81 https://mail.python.org/archives/list/python-dev@python.org/message/YOOQZCFOKEPQ24YHWWLQSJ3RCXFMS7D7/ RedirectTemp /19-82 https://en.wikipedia.org/wiki/Communicating_sequential_processes RedirectTemp /19-83 https://github.com/stackless-dev/stackless/wiki RedirectTemp /19-84 https://www.eveonline.com @@ -792,14 +863,15 @@ RedirectTemp /19-86 https://stackless.readthedocs.io/en/3.6-slp/stackless-python RedirectTemp /19-87 https://doc.pypy.org/en/latest/stackless.html RedirectTemp /19-88 https://greenlet.readthedocs.io/en/latest/ RedirectTemp /19-89 http://www.gevent.org/ -RedirectTemp /19-91 http://thespianpy.com/doc/ -RedirectTemp /19-92 https://pykka.readthedocs.io/en/latest/ -RedirectTemp /19-93 https://www.manning.com/books/rabbitmq-in-action -RedirectTemp /19-94 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ -RedirectTemp /19-95 https://en.wikipedia.org/wiki/OpenCL -RedirectTemp /19-96 https://media.pragprog.com/titles/pb7con/Bonus_Chapter.pdf -RedirectTemp /19-97 https://martinfowler.com/ -RedirectTemp /19-98 https://martinfowler.com/articles/patterns-of-distributed-systems/ +RedirectTemp /19-90 http://thespianpy.com/doc/ +RedirectTemp /19-91 https://pykka.readthedocs.io/en/latest/ +RedirectTemp /19-92 https://www.manning.com/books/rabbitmq-in-action +RedirectTemp /19-93 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ +RedirectTemp /19-94 https://en.wikipedia.org/wiki/OpenCL +RedirectTemp /19-95 https://media.pragprog.com/titles/pb7con/Bonus_Chapter.pdf +RedirectTemp /19-96 https://martinfowler.com/ +RedirectTemp /19-97 https://martinfowler.com/articles/patterns-of-distributed-systems/ +RedirectTemp /19-98 https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/ RedirectTemp /19-99 https://www.oreilly.com/library/view/oscon-2016-video/9781491965153/video247021.html RedirectTemp /19-100 https://www.oreilly.com/library/view/designing-for-scalability/9781449361556/ RedirectTemp /19-101 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy @@ -807,176 +879,191 @@ RedirectTemp /19-102 https://en.wikipedia.org/wiki/KISS_principle RedirectTemp /19-103 https://www.usenix.org/conference/hotos15/workshop-program/presentation/mcsherry ############################################################ 20 RedirectTemp /20-1 https://www.artima.com/weblogs/viewpost.jsp?thread=299551 -RedirectTemp /20-3 https://docs.python.org/3/library/http.server.html -RedirectTemp /20-4 https://www.youtube.com/watch?v=A9e9Cy1UkME -RedirectTemp /20-5 https://www.cia.gov/the-world-factbook/ -RedirectTemp /20-7 https://docs.python-requests.org/en/latest/ -RedirectTemp /20-8 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed -RedirectTemp /20-9 https://docs.python.org/3/library/concurrent.futures.html -RedirectTemp /20-10 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.Executor -RedirectTemp /20-12 https://github.com/noamraph/tqdm -RedirectTemp /20-13 https://www.youtube.com/watch?v=M8Z65tAl5l4 -RedirectTemp /20-14 https://github.com/noamraph/tqdm/blob/master/README.md -RedirectTemp /20-15 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed -RedirectTemp /20-16 https://docs.python.org/3/library/asyncio-task.html#asyncio.as_completed -RedirectTemp /20-17 https://www.cloudflare.com/ -RedirectTemp /20-19 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags -RedirectTemp /20-21 https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418 -RedirectTemp /20-22 https://en.wikipedia.org/wiki/Embarrassingly_parallel -RedirectTemp /20-23 https://pyvideo.org/video/480/pyconau-2010--the-future-is-soon/ -RedirectTemp /20-25 http://www.dabeaz.com/coroutines/ -RedirectTemp /20-26 https://en.wikipedia.org/wiki/POSIX_Threads -RedirectTemp /20-27 https://en.wikipedia.org/wiki/C_dynamic_memory_allocation -RedirectTemp /20-28 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ -RedirectTemp /20-29 https://hexdocs.pm/ecto/getting-started.html +RedirectTemp /20-2 https://docs.python.org/3/library/http.server.html +RedirectTemp /20-3 https://www.youtube.com/watch?v=A9e9Cy1UkME +RedirectTemp /20-4 https://www.cia.gov/the-world-factbook/ +RedirectTemp /20-5 https://docs.python-requests.org/en/latest/ +RedirectTemp /20-6 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor +RedirectTemp /20-7 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed +RedirectTemp /20-8 https://docs.python.org/3/library/concurrent.futures.html +RedirectTemp /20-9 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.Executor +RedirectTemp /20-10 https://github.com/fluentpython/example-code-2e/blob/master/20-executors/getflags/flags2_common.py +RedirectTemp /20-11 https://github.com/noamraph/tqdm +RedirectTemp /20-12 https://www.youtube.com/watch?v=M8Z65tAl5l4 +RedirectTemp /20-13 https://github.com/noamraph/tqdm/blob/master/README.md +RedirectTemp /20-14 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed +RedirectTemp /20-15 https://docs.python.org/3/library/asyncio-task.html#asyncio.as_completed +RedirectTemp /20-16 https://www.cloudflare.com/ +RedirectTemp /20-17 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags +RedirectTemp /20-18 https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418 +RedirectTemp /20-19 https://en.wikipedia.org/wiki/Embarrassingly_parallel +RedirectTemp /20-20 https://pyvideo.org/video/480/pyconau-2010--the-future-is-soon/ +RedirectTemp /20-21 http://www.dabeaz.com/coroutines/ +RedirectTemp /20-22 https://en.wikipedia.org/wiki/POSIX_Threads +RedirectTemp /20-23 https://en.wikipedia.org/wiki/C_dynamic_memory_allocation +RedirectTemp /20-24 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ +RedirectTemp /20-25 https://hexdocs.pm/ecto/getting-started.html ############################################################ 21 RedirectTemp /21-1 https://docs.python.org/3/library/asyncio.html -RedirectTemp /21-6 https://bugs.python.org/issue43216 -RedirectTemp /21-7 https://bugs.python.org/issue36921 -RedirectTemp /21-8 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.getaddrinfo -RedirectTemp /21-9 https://docs.python.org/3/library/socket.html#socket.getaddrinfo -RedirectTemp /21-10 https://docs.python.org/3.10/library/asyncio-eventloop.html#asyncio.get_event_loop -RedirectTemp /21-11 https://www.python.org/dev/peps/pep-0492/#await-expression -RedirectTemp /21-14 https://www.fluentpython.com/extra/classic-coroutines/#yield_from_meaning_sec -RedirectTemp /21-17 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags -RedirectTemp /21-18 https://magicstack.github.io/asyncpg/current/ -RedirectTemp /21-19 https://magicstack.github.io/asyncpg/current/api/index.html#transactions -RedirectTemp /21-21 https://magicstack.github.io/asyncpg/current/api/index.html#transactions -RedirectTemp /21-22 https://magicstack.github.io/asyncpg/current/usage.html#connection-pools -RedirectTemp /21-23 https://gist.github.com/jboner/2841832 -RedirectTemp /21-24 https://en.wikipedia.org/wiki/Network-attached_storage -RedirectTemp /21-25 https://en.wikipedia.org/wiki/Semaphore_(programming) -RedirectTemp /21-26 https://en.wikipedia.org/wiki/Semaphore_(programming) -RedirectTemp /21-27 https://groups.google.com/forum/#!msg/python-tulip/PdAEtwpaJHs/7fqb-Qj2zJoJ -RedirectTemp /21-28 https://tritarget.org/#blog/2012/11/28/the-pyramid-of-doom-a-javascript-style-trap -RedirectTemp /21-29 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor -RedirectTemp /21-30 https://motor.readthedocs.io/en/stable/ -RedirectTemp /21-31 https://emptysqua.re/blog/response-to-asynchronous-python-and-databases/ -RedirectTemp /21-33 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams -RedirectTemp /21-35 https://en.wikipedia.org/wiki/Phaistos_Disc -RedirectTemp /21-36 https://en.wikipedia.org/wiki/Inverted_index -RedirectTemp /21-37 https://fastapi.tiangolo.com/ -RedirectTemp /21-38 https://swagger.io/specification/ -RedirectTemp /21-40 https://asgi.readthedocs.io/en/latest/implementations.html -RedirectTemp /21-41 https://pydantic-docs.helpmanual.io/ -RedirectTemp /21-42 https://doc.traefik.io/traefik/ -RedirectTemp /21-43 https://fastapi.tiangolo.com/project-generation/ -RedirectTemp /21-44 https://fastapi.tiangolo.com/tutorial/response-model/ -RedirectTemp /21-45 https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server -RedirectTemp /21-46 https://github.com/python/typeshed/issues/5535 -RedirectTemp /21-47 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Server.serve_forever -RedirectTemp /21-48 https://docs.python.org/3/library/asyncio-stream.html#asyncio.StreamWriter.close -RedirectTemp /21-49 https://docs.python.org/3/library/asyncio-stream.html#streamwriter -RedirectTemp /21-50 https://docs.python.org/3/library/asyncio-stream.html -RedirectTemp /21-51 https://docs.python.org/3/library/asyncio-protocol.html -RedirectTemp /21-52 https://docs.python.org/3/library/asyncio-protocol.html#tcp-echo-server -RedirectTemp /21-53 https://github.com/aio-libs/aiopg -RedirectTemp /21-54 https://docs.python.org/3/whatsnew/3.8.html#asyncio -RedirectTemp /21-56 https://datatracker.ietf.org/doc/html/rfc6761 -RedirectTemp /21-57 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager -RedirectTemp /21-59 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager -RedirectTemp /21-61 https://docs.python.org/3/library/asyncio-task.html#asyncio.gather -RedirectTemp /21-62 https://curio.readthedocs.io/en/latest/index.html -RedirectTemp /21-63 https://curio.readthedocs.io/en/latest/reference.html#task-groups -RedirectTemp /21-64 https://en.wikipedia.org/wiki/Structured_concurrency -RedirectTemp /21-66 https://www.python.org/dev/peps/pep-0654/#motivation -RedirectTemp /21-67 https://curio.readthedocs.io/en/latest/reference.html#AWAIT -RedirectTemp /21-68 https://www.python-httpx.org/async/#curio -RedirectTemp /21-69 https://github.com/dabeaz/curio/tree/78bca8a6ad677ef51e1568ac7b3e51441ab49c42/examples -RedirectTemp /21-70 https://datatracker.ietf.org/doc/html/rfc8305 -RedirectTemp /21-71 https://trio.readthedocs.io/en/stable/ -RedirectTemp /21-72 https://www.youtube.com/watch?v=M-sc73Y-zQA -RedirectTemp /21-73 https://en.wikipedia.org/wiki/Technical_debt -RedirectTemp /21-74 https://www.youtube.com/watch?v=E-1Y4kSsAFc -RedirectTemp /21-76 https://github.com/dabeaz/curio -RedirectTemp /21-77 https://trio.readthedocs.io/en/stable/ -RedirectTemp /21-78 https://curio.readthedocs.io/en/latest/#curio-university -RedirectTemp /21-79 https://docs.python.org/3/library/asyncio.html -RedirectTemp /21-80 https://bugs.python.org/issue33649 -RedirectTemp /21-82 https://docs.python.org/3/library/asyncio-dev.html -RedirectTemp /21-83 https://www.youtube.com/watch?v=iG6fr81xHKA -RedirectTemp /21-84 https://www.youtube.com/watch?v=F19R_M4Nay4 -RedirectTemp /21-85 https://asherman.io/projects/unsync.html -RedirectTemp /21-86 https://pyladies.com/ -RedirectTemp /21-87 https://www.youtube.com/watch?v=sW76-pRkZk8 -RedirectTemp /21-88 https://www.youtube.com/watch?v=Xbl7XjFYsN4 -RedirectTemp /21-89 https://www.youtube.com/watch?v=02CLD-42VdI -RedirectTemp /21-90 https://micropython.org/ -RedirectTemp /21-91 https://docs.micropython.org/en/latest/library/uasyncio.html -RedirectTemp /21-92 https://www.encode.io/articles/python-async-frameworks-beyond-developer-tribalism -RedirectTemp /21-93 https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ -RedirectTemp /21-94 https://github.com/MagicStack/uvloop -RedirectTemp /21-95 http://magic.io/blog/uvloop-blazing-fast-python-networking/ -RedirectTemp /21-96 https://github.com/MagicStack/httptools -RedirectTemp /21-97 https://docs.aiohttp.org/en/stable/ -RedirectTemp /21-98 https://github.com/wg/wrk -RedirectTemp /21-99 https://twistedmatrix.com/trac/ +RedirectTemp /21-2 https://bugs.python.org/issue43216 +RedirectTemp /21-3 https://bugs.python.org/issue36921 +RedirectTemp /21-4 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.getaddrinfo +RedirectTemp /21-5 https://docs.python.org/3/library/socket.html#socket.getaddrinfo +RedirectTemp /21-6 https://docs.python.org/3.10/library/asyncio-eventloop.html#asyncio.get_event_loop +RedirectTemp /21-7 https://www.python.org/dev/peps/pep-0492/#await-expression +RedirectTemp /21-8 https://www.fluentpython.com/extra/classic-coroutines/#yield_from_meaning_sec +RedirectTemp /21-9 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags +RedirectTemp /21-10 https://magicstack.github.io/asyncpg/current/ +RedirectTemp /21-11 https://magicstack.github.io/asyncpg/current/api/index.html#transactions +RedirectTemp /21-12 https://magicstack.github.io/asyncpg/current/api/index.html#transactions +RedirectTemp /21-13 https://github.com/MagicStack/asyncpg/blob/4d39a05268ce4cc01b00458223a767542da048b8/asyncpg/transaction.py#L57 +RedirectTemp /21-14 https://magicstack.github.io/asyncpg/current/usage.html#connection-pools +RedirectTemp /21-15 https://gist.github.com/jboner/2841832 +RedirectTemp /21-16 https://en.wikipedia.org/wiki/Network-attached_storage +RedirectTemp /21-17 https://en.wikipedia.org/wiki/Semaphore_(programming) +RedirectTemp /21-18 https://en.wikipedia.org/wiki/Semaphore_(programming) +RedirectTemp /21-19 https://groups.google.com/forum/#!msg/python-tulip/PdAEtwpaJHs/7fqb-Qj2zJoJ +RedirectTemp /21-20 https://web.archive.org/web/20151209151711/http://tritarget.org/blog/2012/11/28/the-pyramid-of-doom-a-javascript-style-trap +RedirectTemp /21-21 https://stackoverflow.com/questions/53701841/what-is-the-use-case-for-future-add-done-callback/53710563 +RedirectTemp /21-22 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor +RedirectTemp /21-23 https://motor.readthedocs.io/en/stable/ +RedirectTemp /21-24 https://emptysqua.re/blog/response-to-asynchronous-python-and-databases/ +RedirectTemp /21-25 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams +RedirectTemp /21-26 https://en.wikipedia.org/wiki/Phaistos_Disc +RedirectTemp /21-27 https://en.wikipedia.org/wiki/Inverted_index +RedirectTemp /21-28 https://fastapi.tiangolo.com/ +RedirectTemp /21-29 https://swagger.io/specification/ +RedirectTemp /21-30 https://asgi.readthedocs.io/en/latest/implementations.html +RedirectTemp /21-31 https://pydantic-docs.helpmanual.io/ +RedirectTemp /21-32 https://doc.traefik.io/traefik/ +RedirectTemp /21-33 https://fastapi.tiangolo.com/project-generation/ +RedirectTemp /21-34 https://fastapi.tiangolo.com/tutorial/response-model/ +RedirectTemp /21-35 https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server +RedirectTemp /21-36 https://github.com/python/typeshed/issues/5535 +RedirectTemp /21-37 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Server.serve_forever +RedirectTemp /21-38 https://docs.python.org/3/library/asyncio-stream.html#asyncio.StreamWriter.close +RedirectTemp /21-39 https://docs.python.org/3/library/asyncio-stream.html#streamwriter +RedirectTemp /21-40 https://docs.python.org/3/library/asyncio-stream.html +RedirectTemp /21-41 https://docs.python.org/3/library/asyncio-protocol.html +RedirectTemp /21-42 https://docs.python.org/3/library/asyncio-protocol.html#tcp-echo-server +RedirectTemp /21-43 https://github.com/aio-libs/aiopg +RedirectTemp /21-44 https://docs.python.org/3/whatsnew/3.8.html#asyncio +RedirectTemp /21-45 https://datatracker.ietf.org/doc/html/rfc6761 +RedirectTemp /21-46 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager +RedirectTemp /21-47 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager +RedirectTemp /21-48 https://docs.python.org/3/library/asyncio-task.html#asyncio.gather +RedirectTemp /21-49 https://curio.readthedocs.io/en/latest/index.html +RedirectTemp /21-50 https://curio.readthedocs.io/en/latest/reference.html#task-groups +RedirectTemp /21-51 https://en.wikipedia.org/wiki/Structured_concurrency +RedirectTemp /21-52 https://mail.python.org/archives/list/python-dev@python.org/thread/2ORDAW74LGE3ZI2QETPJRT2ZL7MCCPG2/ +RedirectTemp /21-53 https://www.python.org/dev/peps/pep-0654/#motivation +RedirectTemp /21-54 https://curio.readthedocs.io/en/latest/reference.html#AWAIT +RedirectTemp /21-55 https://www.python-httpx.org/async/#curio +RedirectTemp /21-56 https://github.com/dabeaz/curio/tree/78bca8a6ad677ef51e1568ac7b3e51441ab49c42/examples +RedirectTemp /21-57 https://datatracker.ietf.org/doc/html/rfc8305 +RedirectTemp /21-58 https://trio.readthedocs.io/en/stable/ +RedirectTemp /21-59 https://www.youtube.com/watch?v=M-sc73Y-zQA +RedirectTemp /21-60 https://en.wikipedia.org/wiki/Technical_debt +RedirectTemp /21-61 https://www.youtube.com/watch?v=E-1Y4kSsAFc +RedirectTemp /21-62 https://github.com/dabeaz/curio +RedirectTemp /21-63 https://trio.readthedocs.io/en/stable/ +RedirectTemp /21-64 https://curio.readthedocs.io/en/latest/#curio-university +RedirectTemp /21-65 https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/ +RedirectTemp /21-66 https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ +RedirectTemp /21-67 https://stackoverflow.com/questions/49482969/what-is-the-core-difference-between-asyncio-and-trio +RedirectTemp /21-68 https://docs.python.org/3/library/asyncio.html +RedirectTemp /21-69 https://bugs.python.org/issue33649 +RedirectTemp /21-70 https://docs.python.org/3/library/asyncio-dev.html +RedirectTemp /21-71 https://www.youtube.com/watch?v=iG6fr81xHKA +RedirectTemp /21-72 https://www.youtube.com/watch?v=F19R_M4Nay4 +RedirectTemp /21-73 https://asherman.io/projects/unsync.html +RedirectTemp /21-74 https://pyladies.com/ +RedirectTemp /21-75 https://www.youtube.com/watch?v=sW76-pRkZk8 +RedirectTemp /21-76 https://www.youtube.com/watch?v=Xbl7XjFYsN4 +RedirectTemp /21-77 https://www.youtube.com/watch?v=02CLD-42VdI +RedirectTemp /21-78 https://micropython.org/ +RedirectTemp /21-79 https://docs.micropython.org/en/latest/library/uasyncio.html +RedirectTemp /21-80 https://www.encode.io/articles/python-async-frameworks-beyond-developer-tribalism +RedirectTemp /21-81 https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ +RedirectTemp /21-82 https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ +RedirectTemp /21-83 https://github.com/MagicStack/uvloop +RedirectTemp /21-84 http://magic.io/blog/uvloop-blazing-fast-python-networking/ +RedirectTemp /21-85 https://github.com/MagicStack/httptools +RedirectTemp /21-86 https://docs.aiohttp.org/en/stable/ +RedirectTemp /21-87 https://github.com/wg/wrk +RedirectTemp /21-88 https://twistedmatrix.com/trac/ ############################################################ 22 -RedirectTemp /22-4 https://pypi.org/project/attrdict/ -RedirectTemp /22-5 https://pypi.org/project/addict/ -RedirectTemp /22-7 https://github.com/ActiveState/code/tree/master/recipes/Python/52308_simple_but_handy_collector_bunch_named_stuff -RedirectTemp /22-8 https://docs.python.org/3/library/types.html#types.SimpleNamespace -RedirectTemp /22-9 https://docs.python.org/3/library/argparse.html#argparse.Namespace -RedirectTemp /22-12 https://docs.python.org/3/library/functools.html#functools.cached_property -RedirectTemp /22-13 https://docs.python.org/3/library/functools.html#functools.cached_property -RedirectTemp /22-14 https://bugs.python.org/issue42781 -RedirectTemp /22-15 https://docs.python.org/3/howto/descriptor.html -RedirectTemp /22-16 https://docs.python.org/3/library/threading.html#rlock-objects -RedirectTemp /22-17 https://docs.python.org/3.10/library/functools.html#functools.cached_property -RedirectTemp /22-18 https://www.wsj.com/articles/SB10001424052970203914304576627102996831200 -RedirectTemp /22-19 https://www.youtube.com/watch?v=s35rVw1zskA&feature=youtu.be -RedirectTemp /22-20 https://docs.python.org/3/library/functions.html#dir -RedirectTemp /22-21 https://docs.python.org/3/library/functions.html#hasattr -RedirectTemp /22-22 https://docs.python.org/3.10/reference/datamodel.html#special-method-lookup -RedirectTemp /22-23 https://docs.python.org/3/library/functions.html -RedirectTemp /22-24 https://docs.python.org/3/reference/datamodel.html#customizing-attribute-access -RedirectTemp /22-25 https://docs.python.org/3/reference/datamodel.html#special-method-lookup -RedirectTemp /22-26 https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /22-27 http://wiki.c2.com/?WelcomeVisitors -RedirectTemp /22-28 http://wiki.c2.com/?UniformAccessPrinciple -RedirectTemp /22-29 https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html -RedirectTemp /22-30 http://www.pingo.io/docs/ -RedirectTemp /22-31 https://www.drdobbs.com/javas-new-considered-harmful/184405016 -RedirectTemp /22-32 https://www.python.org/dev/peps/pep-0008/#class-names +RedirectTemp /22-1 https://github.com/fluentpython/example-code-2e/blob/master/22-dyn-attr-prop/oscon/data/osconfeed.json +RedirectTemp /22-2 https://pypi.org/project/attrdict/ +RedirectTemp /22-3 https://pypi.org/project/addict/ +RedirectTemp /22-4 https://github.com/ActiveState/code/tree/master/recipes/Python/52308_simple_but_handy_collector_bunch_named_stuff +RedirectTemp /22-5 https://docs.python.org/3/library/types.html#types.SimpleNamespace +RedirectTemp /22-6 https://docs.python.org/3/library/argparse.html#argparse.Namespace +RedirectTemp /22-7 https://docs.python.org/3/library/multiprocessing.html#multiprocessing.managers.Namespace +RedirectTemp /22-8 https://github.com/fluentpython/example-code-2e/blob/master/22-dyn-attr-prop/oscon/schedule_v2.py +RedirectTemp /22-9 https://docs.python.org/3/library/functools.html#functools.cached_property +RedirectTemp /22-10 https://docs.python.org/3/library/functools.html#functools.cached_property +RedirectTemp /22-11 https://bugs.python.org/issue42781 +RedirectTemp /22-12 https://docs.python.org/3/howto/descriptor.html +RedirectTemp /22-13 https://github.com/python/cpython/blob/e6d0107e13ed957109e79b796984d3d026a8660d/Lib/functools.py#L926 +RedirectTemp /22-14 https://docs.python.org/3/library/threading.html#rlock-objects +RedirectTemp /22-15 https://docs.python.org/3.10/library/functools.html#functools.cached_property +RedirectTemp /22-16 https://www.wsj.com/articles/SB10001424052970203914304576627102996831200 +RedirectTemp /22-17 https://www.youtube.com/watch?v=s35rVw1zskA&feature=youtu.be +RedirectTemp /22-18 https://docs.python.org/3/library/functions.html#dir +RedirectTemp /22-19 https://github.com/python/cpython/blob/19903085c3ad7a17c8047e1556c700f2eb109931/Lib/cmd.py#L214 +RedirectTemp /22-20 https://docs.python.org/3/library/functions.html#hasattr +RedirectTemp /22-21 https://docs.python.org/3.10/reference/datamodel.html#special-method-lookup +RedirectTemp /22-22 https://docs.python.org/3/library/functions.html +RedirectTemp /22-23 https://docs.python.org/3/reference/datamodel.html#customizing-attribute-access +RedirectTemp /22-24 https://docs.python.org/3/reference/datamodel.html#special-method-lookup +RedirectTemp /22-25 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /22-26 http://wiki.c2.com/?WelcomeVisitors +RedirectTemp /22-27 http://wiki.c2.com/?UniformAccessPrinciple +RedirectTemp /22-28 https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html +RedirectTemp /22-29 http://www.pingo.io/docs/ +RedirectTemp /22-30 https://www.drdobbs.com/javas-new-considered-harmful/184405016 +RedirectTemp /22-31 https://www.python.org/dev/peps/pep-0008/#class-names ############################################################ 23 -RedirectTemp /23-2 http://www.aleax.it/goo_pydp.pdf -RedirectTemp /23-3 https://docs.python.org/3.10/reference/datamodel.html#implementing-descriptors -RedirectTemp /23-6 https://docs.python.org/3/howto/descriptor.html -RedirectTemp /23-7 https://docs.python.org/3/howto/ -RedirectTemp /23-8 http://www.aleax.it/Python/nylug05_om.pdf -RedirectTemp /23-9 https://www.youtube.com/watch?v=VOzvpHoYQoo -RedirectTemp /23-11 https://www.python.org/dev/peps/pep-0487/#trait-descriptors -RedirectTemp /23-12 https://dreamsongs.com/RiseOfWorseIsBetter.html -RedirectTemp /23-13 http://web.archive.org/web/20031002184114/www.amk.ca/python/writing/warts.html -RedirectTemp /23-14 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this -RedirectTemp /23-15 http://python-history.blogspot.com/2009/02/adding-support-for-user-defined-classes.html +RedirectTemp /23-1 http://www.aleax.it/goo_pydp.pdf +RedirectTemp /23-2 https://docs.python.org/3.10/reference/datamodel.html#implementing-descriptors +RedirectTemp /23-3 https://docs.python.org/3/howto/descriptor.html +RedirectTemp /23-4 https://docs.python.org/3/howto/ +RedirectTemp /23-5 http://www.aleax.it/Python/nylug05_om.pdf +RedirectTemp /23-6 https://www.youtube.com/watch?v=VOzvpHoYQoo +RedirectTemp /23-7 https://www.python.org/dev/peps/pep-0487/#trait-descriptors +RedirectTemp /23-8 https://dreamsongs.com/RiseOfWorseIsBetter.html +RedirectTemp /23-9 http://web.archive.org/web/20031002184114/www.amk.ca/python/writing/warts.html +RedirectTemp /23-10 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this +RedirectTemp /23-11 http://python-history.blogspot.com/2009/02/adding-support-for-user-defined-classes.html ############################################################ 24 -RedirectTemp /24-2 https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /24-3 https://docs.djangoproject.com/en/3.2/topics/db/models/#meta-options -RedirectTemp /24-4 https://www.python.org/dev/peps/pep-3155/ -RedirectTemp /24-7 https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions -RedirectTemp /24-8 https://go.dev/tour/basics/12 -RedirectTemp /24-9 https://bugs.python.org/issue42102 -RedirectTemp /24-11 https://www.python.org/dev/peps/pep-0557/#abstract -RedirectTemp /24-12 https://github.com/python/cpython/blob/3.9/Lib/dataclasses.py -RedirectTemp /24-13 https://docs.python.org/3/reference/datamodel.html#creating-the-class-object -RedirectTemp /24-15 https://mail.python.org/pipermail/python-list/2002-December/134521.html -RedirectTemp /24-16 https://www.oreilly.com/library/view/python-in-a/9781491913833/ -RedirectTemp /24-17 https://mail.python.org/pipermail/python-list/2002-July/162558.html -RedirectTemp /24-18 https://github.com/fluentpython/example-code/tree/master/21-class-metaprog/bulkfood -RedirectTemp /24-19 https://en.wikipedia.org/wiki/Principle_of_least_astonishment -RedirectTemp /24-21 https://en.wikipedia.org/wiki/Trait_(computer_programming) -RedirectTemp /24-22 https://en.wikipedia.org/wiki/Aspect-oriented_programming -RedirectTemp /24-23 https://dhh.dk/arc/000416.html -RedirectTemp /24-24 https://github.com/cjrh/autoslot -RedirectTemp /24-25 https://docs.python.org/3/reference/datamodel.html#customizing-class-creation -RedirectTemp /24-26 https://docs.python.org/3/library/functions.html#type -RedirectTemp /24-27 https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /24-28 https://docs.python.org/3/library/types.html -RedirectTemp /24-29 https://www.python.org/dev/peps/pep-3129/ -RedirectTemp /24-30 https://www.youtube.com/watch?v=cAGliEJV9_o -RedirectTemp /24-31 https://docs.python.org/3/library/functools.html#functools.total_ordering -RedirectTemp /24-33 https://www.oreilly.com/library/view/python-in-a/9781491913833/ -RedirectTemp /24-36 https://www.python.org/download/releases/2.2.3/descrintro/ -RedirectTemp /24-42 https://github.com/lihaoyi/macropy -RedirectTemp /24-43 https://people.eecs.berkeley.edu/~bh/ss-toc2.html +RedirectTemp /24-1 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /24-2 https://docs.djangoproject.com/en/3.2/topics/db/models/#meta-options +RedirectTemp /24-3 https://www.python.org/dev/peps/pep-3155/ +RedirectTemp /24-4 https://github.com/python/cpython/blob/3.9/Lib/collections/__init__.py +RedirectTemp /24-5 https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions +RedirectTemp /24-6 https://go.dev/tour/basics/12 +RedirectTemp /24-7 https://bugs.python.org/issue42102 +RedirectTemp /24-8 https://docs.python.org/3/reference/datamodel.html#object.__init_subclass__ +RedirectTemp /24-9 https://www.python.org/dev/peps/pep-0557/#abstract +RedirectTemp /24-10 https://github.com/python/cpython/blob/3.9/Lib/dataclasses.py +RedirectTemp /24-11 https://docs.python.org/3/reference/datamodel.html#creating-the-class-object +RedirectTemp /24-12 https://mail.python.org/pipermail/python-list/2002-December/134521.html +RedirectTemp /24-13 https://mail.python.org/pipermail/python-list/2002-July/162558.html +RedirectTemp /24-14 https://github.com/fluentpython/example-code/tree/master/21-class-metaprog/bulkfood +RedirectTemp /24-15 https://en.wikipedia.org/wiki/Principle_of_least_astonishment +RedirectTemp /24-16 https://docs.python.org/3/reference/datamodel.html#object.__class_getitem__ +RedirectTemp /24-17 https://en.wikipedia.org/wiki/Trait_(computer_programming) +RedirectTemp /24-18 https://en.wikipedia.org/wiki/Aspect-oriented_programming +RedirectTemp /24-19 https://dhh.dk/arc/000416.html +RedirectTemp /24-20 https://github.com/cjrh/autoslot +RedirectTemp /24-21 https://docs.python.org/3/reference/datamodel.html#customizing-class-creation +RedirectTemp /24-22 https://docs.python.org/3/library/functions.html#type +RedirectTemp /24-23 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /24-24 https://docs.python.org/3/library/types.html +RedirectTemp /24-25 https://www.python.org/dev/peps/pep-3129/ +RedirectTemp /24-26 https://www.youtube.com/watch?v=cAGliEJV9_o +RedirectTemp /24-27 https://docs.python.org/3/library/functools.html#functools.total_ordering +RedirectTemp /24-28 https://www.python.org/download/releases/2.2.3/descrintro/ +RedirectTemp /24-29 https://github.com/lihaoyi/macropy +RedirectTemp /24-30 https://people.eecs.berkeley.edu/~bh/ss-toc2.html diff --git a/links/custom.htaccess b/links/custom.htaccess index 52649db..b9bc509 100644 --- a/links/custom.htaccess +++ b/links/custom.htaccess @@ -25,6 +25,17 @@ RedirectTemp /norvigdp http://norvig.com/design-patterns/ RedirectTemp /nsphere https://en.wikipedia.org/wiki/N-sphere RedirectTemp /oldcoro https://www.fluentpython.com/extra/classic-coroutines/ RedirectTemp /pandas https://pandas.pydata.org/ +RedirectTemp /pycook3 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ +RedirectTemp /pynut3 https://www.oreilly.com/library/view/python-in-a/9781491913833/ +RedirectTemp /pypydif https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /shed4051 https://github.com/python/typeshed/issues/4051 +RedirectTemp /slatkin https://effectivepython.com/ +RedirectTemp /specattr https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine +RedirectTemp /typing https://docs.python.org/3/library/typing.html +RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ + +# Python Enhancement Proposals RedirectTemp /pep218 https://www.python.org/dev/peps/pep-0218/ RedirectTemp /pep227 https://www.python.org/dev/peps/pep-0227/ RedirectTemp /pep255 https://www.python.org/dev/peps/pep-0255/ @@ -100,10 +111,3 @@ RedirectTemp /pep3141 https://www.python.org/dev/peps/pep-3141/ RedirectTemp /pep3148 https://www.python.org/dev/peps/pep-3148/ RedirectTemp /pep3155 https://www.python.org/dev/peps/pep-3155/ RedirectTemp /pep3333 https://www.python.org/dev/peps/pep-3333/ -RedirectTemp /pypydif https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types -RedirectTemp /shed4051 https://github.com/python/typeshed/issues/4051 -RedirectTemp /slatkin https://effectivepython.com/ -RedirectTemp /specattr https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine -RedirectTemp /typing https://docs.python.org/3/library/typing.html -RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ From 08318a657a683c7d80b06a94aeee3f8f36ffbb25 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 17 Jan 2022 15:22:20 -0300 Subject: [PATCH 097/127] harmless edits in .htaccess --- links/FPY.LI.htaccess | 12 ++++++------ links/custom.htaccess | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/links/FPY.LI.htaccess b/links/FPY.LI.htaccess index 9f960f1..6e324d0 100644 --- a/links/FPY.LI.htaccess +++ b/links/FPY.LI.htaccess @@ -1,19 +1,20 @@ ErrorDocument 404 /404.html # main resources +RedirectTemp /book https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ RedirectTemp /code https://github.com/fluentpython/example-code-2e RedirectTemp /home https://www.fluentpython.com/ # URLs mentioned at least three times RedirectTemp /bisect https://www.fluentpython.com/extra/ordered-sequences-with-bisect/ -RedirectTemp /cardxvi https://www.python.org/dev/peps/pep-0484/#the-numeric-tower +RedirectTemp /cardxvi https://www.python.org/dev/peps/pep-0484/#the-numeric-tower RedirectTemp /collec https://docs.python.org/3/library/collections.html RedirectTemp /dask https://dask.org/ RedirectTemp /dtmodel https://docs.python.org/3/reference/datamodel.html RedirectTemp /descr101 https://www.python.org/download/releases/2.2.3/descrintro/ RedirectTemp /descrhow https://docs.python.org/3/howto/descriptor.html RedirectTemp /doctest https://docs.python.org/3/library/doctest.html -RedirectTemp /effectpy https://effectivepython.com/ +RedirectTemp /effectpy https://effectivepython.com/ RedirectTemp /fmtspec https://docs.python.org/3/library/string.html#formatspec RedirectTemp /gunicorn https://gunicorn.org/ RedirectTemp /hashint https://www.fluentpython.com/extra/internals-of-sets-and-dicts/ @@ -25,13 +26,12 @@ RedirectTemp /norvigdp http://norvig.com/design-patterns/ RedirectTemp /nsphere https://en.wikipedia.org/wiki/N-sphere RedirectTemp /oldcoro https://www.fluentpython.com/extra/classic-coroutines/ RedirectTemp /pandas https://pandas.pydata.org/ -RedirectTemp /pycook3 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ -RedirectTemp /pynut3 https://www.oreilly.com/library/view/python-in-a/9781491913833/ +RedirectTemp /pycook3 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ +RedirectTemp /pynut3 https://www.oreilly.com/library/view/python-in-a/9781491913833/ RedirectTemp /pypydif https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types RedirectTemp /shed4051 https://github.com/python/typeshed/issues/4051 -RedirectTemp /slatkin https://effectivepython.com/ RedirectTemp /specattr https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine +RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine RedirectTemp /typing https://docs.python.org/3/library/typing.html RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ diff --git a/links/custom.htaccess b/links/custom.htaccess index b9bc509..a13b9f8 100644 --- a/links/custom.htaccess +++ b/links/custom.htaccess @@ -1,19 +1,20 @@ ErrorDocument 404 /404.html # main resources +RedirectTemp /book https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ RedirectTemp /code https://github.com/fluentpython/example-code-2e RedirectTemp /home https://www.fluentpython.com/ # URLs mentioned at least three times RedirectTemp /bisect https://www.fluentpython.com/extra/ordered-sequences-with-bisect/ -RedirectTemp /cardxvi https://www.python.org/dev/peps/pep-0484/#the-numeric-tower +RedirectTemp /cardxvi https://www.python.org/dev/peps/pep-0484/#the-numeric-tower RedirectTemp /collec https://docs.python.org/3/library/collections.html RedirectTemp /dask https://dask.org/ RedirectTemp /dtmodel https://docs.python.org/3/reference/datamodel.html RedirectTemp /descr101 https://www.python.org/download/releases/2.2.3/descrintro/ RedirectTemp /descrhow https://docs.python.org/3/howto/descriptor.html RedirectTemp /doctest https://docs.python.org/3/library/doctest.html -RedirectTemp /effectpy https://effectivepython.com/ +RedirectTemp /effectpy https://effectivepython.com/ RedirectTemp /fmtspec https://docs.python.org/3/library/string.html#formatspec RedirectTemp /gunicorn https://gunicorn.org/ RedirectTemp /hashint https://www.fluentpython.com/extra/internals-of-sets-and-dicts/ @@ -25,13 +26,12 @@ RedirectTemp /norvigdp http://norvig.com/design-patterns/ RedirectTemp /nsphere https://en.wikipedia.org/wiki/N-sphere RedirectTemp /oldcoro https://www.fluentpython.com/extra/classic-coroutines/ RedirectTemp /pandas https://pandas.pydata.org/ -RedirectTemp /pycook3 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ -RedirectTemp /pynut3 https://www.oreilly.com/library/view/python-in-a/9781491913833/ +RedirectTemp /pycook3 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ +RedirectTemp /pynut3 https://www.oreilly.com/library/view/python-in-a/9781491913833/ RedirectTemp /pypydif https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types RedirectTemp /shed4051 https://github.com/python/typeshed/issues/4051 -RedirectTemp /slatkin https://effectivepython.com/ RedirectTemp /specattr https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine +RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine RedirectTemp /typing https://docs.python.org/3/library/typing.html RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ From b39c1c1f049ec5b0c6e1686cda20b39f45072824 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 16 Feb 2022 14:40:49 -0300 Subject: [PATCH 098/127] added bdfl short URL --- links/FPY.LI.htaccess | 3 +++ links/README.md | 5 ++++- links/custom.htaccess | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/links/FPY.LI.htaccess b/links/FPY.LI.htaccess index 6e324d0..d38c973 100644 --- a/links/FPY.LI.htaccess +++ b/links/FPY.LI.htaccess @@ -35,6 +35,9 @@ RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.C RedirectTemp /typing https://docs.python.org/3/library/typing.html RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ +# URLs added during QA of the Second Edition +RedirectTemp /bdfl https://www.artima.com/weblogs/viewpost.jsp?thread=235725 + # Python Enhancement Proposals RedirectTemp /pep218 https://www.python.org/dev/peps/pep-0218/ RedirectTemp /pep227 https://www.python.org/dev/peps/pep-0227/ diff --git a/links/README.md b/links/README.md index 09c808a..6da595c 100644 --- a/links/README.md +++ b/links/README.md @@ -12,7 +12,10 @@ I replaced almost all URLs in the book with shortened versions that go through t The site has an `.htaccess` file with *temporary* redirects. When I find out a link is stale, I can thange the redirect in `.htaccess` to a new target, -so the link in the book is back in service through the updated redirect. +which may be a link to copy in the Internet Archive's +[Wayback Machine](https://archive.org/web/) +o the link in the book is back in service through the updated redirect. + ## Help wanted diff --git a/links/custom.htaccess b/links/custom.htaccess index a13b9f8..9e070f1 100644 --- a/links/custom.htaccess +++ b/links/custom.htaccess @@ -35,6 +35,9 @@ RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.C RedirectTemp /typing https://docs.python.org/3/library/typing.html RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ +# URLs added during QA of the Second Edition +RedirectTemp /bdfl https://www.artima.com/weblogs/viewpost.jsp?thread=235725 + # Python Enhancement Proposals RedirectTemp /pep218 https://www.python.org/dev/peps/pep-0218/ RedirectTemp /pep227 https://www.python.org/dev/peps/pep-0227/ From 5f2c3abfc76c906003cb35dbb4e50bd0cb6e0206 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 16 Feb 2022 14:46:28 -0300 Subject: [PATCH 099/127] added bdfl short URL --- links/FPY.LI.htaccess | 2 +- links/custom.htaccess | 2 +- links/deploy.sh | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100755 links/deploy.sh diff --git a/links/FPY.LI.htaccess b/links/FPY.LI.htaccess index d38c973..44d2530 100644 --- a/links/FPY.LI.htaccess +++ b/links/FPY.LI.htaccess @@ -35,7 +35,7 @@ RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.C RedirectTemp /typing https://docs.python.org/3/library/typing.html RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ -# URLs added during QA of the Second Edition +# URL added during QA of the Second Edition RedirectTemp /bdfl https://www.artima.com/weblogs/viewpost.jsp?thread=235725 # Python Enhancement Proposals diff --git a/links/custom.htaccess b/links/custom.htaccess index 9e070f1..ad10802 100644 --- a/links/custom.htaccess +++ b/links/custom.htaccess @@ -35,7 +35,7 @@ RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.C RedirectTemp /typing https://docs.python.org/3/library/typing.html RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ -# URLs added during QA of the Second Edition +# URL added during QA of the Second Edition RedirectTemp /bdfl https://www.artima.com/weblogs/viewpost.jsp?thread=235725 # Python Enhancement Proposals diff --git a/links/deploy.sh b/links/deploy.sh new file mode 100755 index 0000000..ced9bd9 --- /dev/null +++ b/links/deploy.sh @@ -0,0 +1,2 @@ +#!/bin/bash +scp FPY.LI.htaccess dh_i4p2ka@fpy.li:~/fpy.li/.htaccess From 536e68686fed2c51a29b6dc0e2acbff7aeb3ee3f Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Mon, 21 Feb 2022 18:21:28 -0300 Subject: [PATCH 100/127] updated link to String Format String Syntax in Python 3 --- links/FPY.LI.htaccess | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/links/FPY.LI.htaccess b/links/FPY.LI.htaccess index 44d2530..cc779db 100644 --- a/links/FPY.LI.htaccess +++ b/links/FPY.LI.htaccess @@ -152,7 +152,7 @@ RedirectTemp /a-18 https://www.python.org/doc/essays/ RedirectTemp /1-1 http://hugunin.net/story_of_jython.html RedirectTemp /1-2 https://www.oreilly.com/library/view/jython-essentials/9781449397364/ RedirectTemp /1-3 https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers -RedirectTemp /1-4 https://docs.python.org/2/library/string.html#format-string-syntax +RedirectTemp /1-4 https://docs.python.org/3.10/library/string.html#format-string-syntax RedirectTemp /1-5 https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr RedirectTemp /1-6 https://docs.python.org/3/library/stdtypes.html#truth RedirectTemp /1-7 https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists From a40bd03b0eb9e88884b93377dea7238038893916 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 9 Mar 2022 18:35:41 -0300 Subject: [PATCH 101/127] renumbered book parts --- README.md | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 96273ac..234c8da 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,7 @@ # Fluent Python 2e example code -Example code for the book **Fluent Python, 2nd edition** by Luciano Ramalho (O'Reilly, 2021). +Example code for the book **Fluent Python, Second Edition** by Luciano Ramalho (O'Reilly, 2022). -> **BEWARE**: This is a work in progress! -> -> * Code here may change and disappear without warning. -> -> * Major reorganizations may happen at any time. -> -> * No promises. No guarantees. Use at own risk. ## Table of Contents @@ -16,37 +9,37 @@ All chapters are undergoing review and updates, including significant rewrites i New chapters in **Fluent Python 2e** are marked with 🆕. -🚨 This table of contents is subject to change at any time until the book goes to the printer. +> 🚨  This table of contents is subject to change at any time until the book goes to the printer.
+Latest change: Old **Part I—Prologue** merged into new **Part I—Data Structures**; parts renumbered accordingly; chapter numbers unchanged. Part / Chapter #|Title|Directory|1st ed. Chapter # ---:|---|---|:---: -**I – Prologue**| +**I – Data Structures**| 1|The Python Data Model|[01-data-model](01-data-model)|1 -**II – Data Structures**| 2|An Array of Sequences|[02-array-seq](02-array-seq)|2 3|Dictionaries and Sets|[03-dict-set](03-dict-set)|3 4|Unicode Text versus Bytes|[04-text-byte](04-text-byte)|4 5|Data Class Builders|[05-data-classes](05-data-classes)|🆕 6|Object References, Mutability, and Recycling|[06-obj-ref](06-obj-ref)|8 -**III – Functions as Objects**| +**II – Functions as Objects**| 7|Funcions as First-Class Objects|[07-1class-func](07-1class-func)|5 -8|Type Hints in Function Definitions|[08-def-type-hints](08-def-type-hints)|🆕 -9|Function Decorators and Closures|[09-closure-deco](09-closure-deco)|7 +8|Type Hints in Functions|[08-def-type-hints](08-def-type-hints)|🆕 +9|Decorators and Closures|[09-closure-deco](09-closure-deco)|7 10|Design Patterns with First-Class Functions|[10-dp-1class-func](10-dp-1class-func)|6 -**IV – Object-Oriented Idioms**| +**III – Object-Oriented Idioms**| 11|A Pythonic Object|[11-pythonic-obj](11-pythonic-obj)|9 -12|Sequence Hacking, Hashing, and Slicing|[12-seq-hacking](12-seq-hacking)|10 +12|Special Methods for Sequences|[12-seq-hacking](12-seq-hacking)|10 13|Interfaces, Protocols, and ABCs|[13-protocl-abc](13-protocol-abc)|11 -14|Inheritance: For Good or For Worse|[14-inheritance](14-inheritance)|12 +14|Inheritance: For Better or For Worse|[14-inheritance](14-inheritance)|12 15|More About Type Hints|[15-more-types](15-more-types)|🆕 -16|Operator Overloading: Doing It Right|[16-op-overloading](16-op-overloading)|13 -**V – Control Flow**| +16|Operator Overloading|[16-op-overloading](16-op-overloading)|13 +**IV – Control Flow**| 17|Iterators, Generators, and Classic Coroutines|[17-it-generator](17-it-generator)|14 -18|Context Managers and else Blocks|[18-with-match](18-with-match)|15 +18|with, match, and else Blocks|[18-with-match](18-with-match)|15 19|Concurrency Models in Python|[19-concurrency](19-concurrency)|🆕 20|Concurrent Executors|[20-executors](20-executors)|17 21|Asynchronous Programming|[21-async](21-async)|18 -**VI – Metaprogramming**| +**V – Metaprogramming**| 22|Dynamic Attributes and Properties|[22-dyn-attr-prop](22-dyn-attr-prop)|19 23|Attribute Descriptors|[23-descriptor](23-descriptor)|20 24|Class Metaprogramming|[24-class-metaprog](24-class-metaprog)|21 From 8158222f2224c95ae57a4f8599073245ee94dc06 Mon Sep 17 00:00:00 2001 From: Scott McCormack Date: Mon, 14 Mar 2022 21:58:48 +0800 Subject: [PATCH 102/127] 2nd edition updates --- 02-array-seq/array-seq.ipynb | 1834 ++++++++++++++++++++++------------ 1 file changed, 1201 insertions(+), 633 deletions(-) diff --git a/02-array-seq/array-seq.ipynb b/02-array-seq/array-seq.ipynb index 032d811..afdbf6b 100644 --- a/02-array-seq/array-seq.ipynb +++ b/02-array-seq/array-seq.ipynb @@ -9,12 +9,14 @@ "**Sections with code snippets in this chapter:**\n", "\n", "* [List Comprehensions and Generator Expressions](#List-Comprehensions-and-Generator-Expressions)\n", + "* [Tuples Are Not Just Immutable Lists](#Tuples-Are-Not-Just-Immutable-Lists)\n", + "* [Unpacking sequences and iterables](#Unpacking-sequences-and-iterables)\n", + "* [Pattern Matching with Sequences](#Pattern-Matching-with-Sequences)\n", "* [Slicing](#Slicing)\n", - "* [Building Lists of Lists](#Building-Lists-of-Lists)\n", + "* [Using + and * with Sequences](#Using-+-and-*-with-Sequences)\n", "* [Augmented Assignment with Sequences](#Augmented-Assignment-with-Sequences)\n", "* [list.sort and the sorted Built-In Function](#list.sort-and-the-sorted-Built-In-Function)\n", - "* [Managing Ordered Sequences with bisect](#Managing-Ordered-Sequences-with-bisect)\n", - "* [Arrays](#Arrays)\n", + "* [When a List Is Not the Answer](#When-a-List-Is-Not-the-Answer)\n", "* [Memory Views](#Memory-Views)\n", "* [NumPy and SciPy](#NumPy-and-SciPy)\n", "* [Deques and Other Queues](#Deques-and-Other-Queues)\n", @@ -42,9 +44,7 @@ "outputs": [ { "data": { - "text/plain": [ - "[36, 162, 163, 165, 8364, 164]" - ] + "text/plain": "[36, 162, 163, 165, 8364, 164]" }, "execution_count": 1, "metadata": {}, @@ -65,7 +65,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-2. Build a list of Unicode codepoints from a string, take 2" + "#### Example 2-2. Build a list of Unicode codepoints from a string, using a listcomp" ] }, { @@ -75,9 +75,7 @@ "outputs": [ { "data": { - "text/plain": [ - "[36, 162, 163, 165, 8364, 164]" - ] + "text/plain": "[36, 162, 163, 165, 8364, 164]" }, "execution_count": 2, "metadata": {}, @@ -106,9 +104,7 @@ "outputs": [ { "data": { - "text/plain": [ - "'ABC'" - ] + "text/plain": "'ABC'" }, "execution_count": 3, "metadata": {}, @@ -128,9 +124,7 @@ "outputs": [ { "data": { - "text/plain": [ - "[65, 66, 67]" - ] + "text/plain": "[65, 66, 67]" }, "execution_count": 4, "metadata": {}, @@ -141,6 +135,30 @@ "codes" ] }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [ + { + "data": { + "text/plain": "67" + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "codes = [last := ord(c) for c in x]\n", + "last" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, { "cell_type": "markdown", "metadata": {}, @@ -150,214 +168,958 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": {}, + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "[162, 163, 165, 8364, 164]" + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "symbols = '$¢£¥€¤'\n", + "beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]\n", + "beyond_ascii" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "[162, 163, 165, 8364, 164]" + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))\n", + "beyond_ascii" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2-4. Cartesian product using a list comprehension" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "[('black', 'S'),\n ('black', 'M'),\n ('black', 'L'),\n ('white', 'S'),\n ('white', 'M'),\n ('white', 'L')]" + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "colors = ['black', 'white']\n", + "sizes = ['S', 'M', 'L']\n", + "tshirts = [(color, size) for color in colors for size in sizes]\n", + "tshirts" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('black', 'S')\n", + "('black', 'M')\n", + "('black', 'L')\n", + "('white', 'S')\n", + "('white', 'M')\n", + "('white', 'L')\n" + ] + } + ], + "source": [ + "for color in colors:\n", + " for size in sizes:\n", + " print((color, size))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "[('black', 'S'),\n ('black', 'M'),\n ('black', 'L'),\n ('white', 'S'),\n ('white', 'M'),\n ('white', 'L')]" + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "shirts = [(color, size) for size in sizes\n", + " for color in colors]\n", + "tshirts" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2-5. Initializing a tuple and an array from a generator expression" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "(36, 162, 163, 165, 8364, 164)" + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "symbols = '$¢£¥€¤'\n", + "tuple(ord(symbol) for symbol in symbols)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "array('I', [36, 162, 163, 165, 8364, 164])" + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import array\n", + "\n", + "array.array('I', (ord(symbol) for symbol in symbols))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2-6. Cartesian product in a generator expression" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "black S\n", + "black M\n", + "black L\n", + "white S\n", + "white M\n", + "white L\n" + ] + } + ], + "source": [ + "colors = ['black', 'white']\n", + "sizes = ['S', 'M', 'L']\n", + "\n", + "for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes):\n", + " print(tshirt)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Tuples Are Not Just Immutable Lists" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + }, + "execution_count": 73 + }, + { + "cell_type": "markdown", + "source": [ + "#### Example 2-7. Tuples used as records" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 14, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "BRA/CE342567\n", + "ESP/XDA205856\n", + "USA/31195855\n" + ] + } + ], + "source": [ + "lax_coordinates = (33.9425, -118.408056)\n", + "city, year, pop, chg, area = ('Tokyo', 2003, 32_450, 0.66, 8014)\n", + "traveler_ids = [('USA', '31195855'), ('BRA', 'CE342567'), ('ESP', 'XDA205856')]\n", + "\n", + "for passport in sorted(traveler_ids):\n", + " print('%s/%s' % passport)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 15, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "USA\n", + "BRA\n", + "ESP\n" + ] + } + ], + "source": [ + "for country, _ in traveler_ids:\n", + " print(country)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Tuples as Immutable Lists" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 16, + "outputs": [ + { + "data": { + "text/plain": "True" + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = (10, 'alpha', [1, 2])\n", + "b = (10, 'alpha', [1, 2])\n", + "a == b" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 17, + "outputs": [ + { + "data": { + "text/plain": "False" + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b[-1].append(99)\n", + "a == b" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 18, + "outputs": [ + { + "data": { + "text/plain": "(10, 'alpha', [1, 2, 99])" + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 19, + "outputs": [ + { + "data": { + "text/plain": "True" + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def fixed(o):\n", + " try:\n", + " hash(o)\n", + " except TypeError:\n", + " return False\n", + " return True\n", + "\n", + "\n", + "tf = (10, 'alpha', (1, 2)) # Contains no mutable items\n", + "tm = (10, 'alpha', [1, 2]) # Contains a mutable item (list)\n", + "fixed(tf)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 20, + "outputs": [ + { + "data": { + "text/plain": "False" + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fixed(tm)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Unpacking sequences and iterables" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 21, + "outputs": [ + { + "data": { + "text/plain": "33.9425" + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lax_coordinates = (33.9425, -118.408056)\n", + "latitude, longitude = lax_coordinates # unpacking\n", + "latitude" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 22, + "outputs": [ + { + "data": { + "text/plain": "-118.408056" + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "longitude" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 23, + "outputs": [ + { + "data": { + "text/plain": "(2, 4)" + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "divmod(20, 8)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 24, + "outputs": [ + { + "data": { + "text/plain": "(2, 4)" + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t = (20, 8)\n", + "divmod(*t)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 25, + "outputs": [ + { + "data": { + "text/plain": "(2, 4)" + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "quotient, remainder = divmod(*t)\n", + "quotient, remainder" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 26, + "outputs": [ + { + "data": { + "text/plain": "'id_rsa.pub'" + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import os\n", + "\n", + "_, filename = os.path.split('/home/luciano/.ssh/id_rsa.pub')\n", + "filename" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Using * to grab excess items" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 27, + "outputs": [ + { + "data": { + "text/plain": "(0, 1, [2, 3, 4])" + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a, b, *rest = range(5)\n", + "a, b, rest" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 28, + "outputs": [ + { + "data": { + "text/plain": "(0, 1, [2])" + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a, b, *rest = range(3)\n", + "a, b, rest" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 29, "outputs": [ { "data": { - "text/plain": [ - "[162, 163, 165, 8364, 164]" - ] + "text/plain": "(0, 1, [])" }, - "execution_count": 5, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "symbols = '$¢£¥€¤'\n", - "beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]\n", - "beyond_ascii" - ] + "a, b, *rest = range(2)\n", + "a, b, rest" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 6, - "metadata": {}, + "execution_count": 30, "outputs": [ { "data": { - "text/plain": [ - "[162, 163, 165, 8364, 164]" - ] + "text/plain": "(0, [1, 2], 3, 4)" }, - "execution_count": 6, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))\n", - "beyond_ascii" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Example 2-4. Cartesian product using a list comprehension" - ] + "a, *body, c, d = range(5)\n", + "a, body, c, d" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 7, - "metadata": {}, + "execution_count": 31, "outputs": [ { "data": { - "text/plain": [ - "[('black', 'S'),\n", - " ('black', 'M'),\n", - " ('black', 'L'),\n", - " ('white', 'S'),\n", - " ('white', 'M'),\n", - " ('white', 'L')]" - ] + "text/plain": "([0, 1], 2, 3, 4)" }, - "execution_count": 7, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "colors = ['black', 'white']\n", - "sizes = ['S', 'M', 'L']\n", - "tshirts = [(color, size) for color in colors for size in sizes]\n", - "tshirts" - ] + "*head, b, c, d = range(5)\n", + "head, b, c, d" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Unpacking with * in function calls and sequence literals" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } }, { "cell_type": "code", - "execution_count": 8, - "metadata": {}, + "execution_count": 32, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "('black', 'S')\n", - "('black', 'M')\n", - "('black', 'L')\n", - "('white', 'S')\n", - "('white', 'M')\n", - "('white', 'L')\n" - ] + "data": { + "text/plain": "(1, 2, 3, 4, (5, 6))" + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "for color in colors:\n", - " for size in sizes:\n", - " print((color, size))" - ] + "def fun(a, b, c, d, *rest):\n", + " return a, b, c, d, rest\n", + "\n", + "\n", + "fun(*[1, 2], 3, *range(4, 7))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 9, - "metadata": {}, + "execution_count": 33, "outputs": [ { "data": { - "text/plain": [ - "[('black', 'S'),\n", - " ('black', 'M'),\n", - " ('black', 'L'),\n", - " ('white', 'S'),\n", - " ('white', 'M'),\n", - " ('white', 'L')]" - ] + "text/plain": "(0, 1, 2, 3, 4)" }, - "execution_count": 9, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "shirts = [(color, size) for size in sizes\n", - " for color in colors]\n", - "tshirts" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Example 2-5. Initializing a tuple and an array from a generator expression" - ] + "*range(4), 4" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 10, - "metadata": {}, + "execution_count": 34, "outputs": [ { "data": { - "text/plain": [ - "(36, 162, 163, 165, 8364, 164)" - ] + "text/plain": "[0, 1, 2, 3, 4]" }, - "execution_count": 10, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "symbols = '$¢£¥€¤'\n", - "tuple(ord(symbol) for symbol in symbols)" - ] + "[*range(4), 4]" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 11, - "metadata": {}, + "execution_count": 35, "outputs": [ { "data": { - "text/plain": [ - "array('I', [36, 162, 163, 165, 8364, 164])" - ] + "text/plain": "{0, 1, 2, 3, 4, 5, 6, 7}" }, - "execution_count": 11, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "import array\n", - "array.array('I', (ord(symbol) for symbol in symbols))" - ] + "{*range(4), 4, *(5, 6, 7)}" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "markdown", - "metadata": {}, "source": [ - "#### Example 2-6. Cartesian product in a generator expression" - ] + "### Nested unpacking\n", + "#### Example 2-8. Unpacking nested tuples to access the longitude\n", + "\n", + "[02-array-seq/metro_lat_lon.py](02-array-seq/metro_lat_lon.py)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Pattern Matching with Sequences\n", + "#### Example 2-9. Method from an imaginary Robot class" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } }, { "cell_type": "code", - "execution_count": 12, - "metadata": {}, + "execution_count": 36, + "outputs": [], + "source": [ + "# def handle_command(self, message):\n", + "# match message:\n", + "# case ['BEEPER', frequency, times]:\n", + "# self.beep(times, frequency)\n", + "# case ['NECK', angle]:\n", + "# self.rotate_neck(angle)\n", + "# case ['LED', ident, intensity]:\n", + "# self.leds[ident].set_brightness(ident, intensity)\n", + "# case ['LED', ident, red, green, blue]:\n", + "# self.leds[ident].set_color(ident, red, green, blue)\n", + "# case _:\n", + "# raise InvalidCommand(message)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "#### Example 2-10. Destructuring nested tuples—requires Python ≥ 3.10.\n", + "[02-array-seq/match_lat_lon.py](02-array-seq/match_lat_lon.py)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 37, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "black S\n", - "black M\n", - "black L\n", - "white S\n", - "white M\n", - "white L\n" + " | latitude | longitude\n", + "Mexico City | 19.4333 | -99.1333\n", + "New York-Newark | 40.8086 | -74.0204\n", + "São Paulo | -23.5478 | -46.6358\n" ] } ], "source": [ - "colors = ['black', 'white']\n", - "sizes = ['S', 'M', 'L']\n", + "metro_areas = [\n", + " ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),\n", + " ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),\n", + " ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),\n", + " ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),\n", + " ('São Paulo', 'BR', 19.649, (-23.547778, -46.635833)),\n", + "]\n", "\n", - "for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes):\n", - " print(tshirt)" - ] + "def main():\n", + " print(f'{\"\":15} | {\"latitude\":>9} | {\"longitude\":>9}')\n", + " for record in metro_areas:\n", + " match record:\n", + " case [name, _, _, (lat, lon)] if lon <= 0:\n", + " print(f'{name:15} | {lat:9.4f} | {lon:9.4f}')\n", + "main()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Pattern Matching Sequences in an Interpreter\n", + "#### Example 2-11. Matching patterns without match/case.\n", + "[02-array-seq/lispy/py3.9/lis.py](02-array-seq/lispy/py3.9/lis.py)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "#### Example 2-12. Pattern matching with match/case—requires Python ≥ 3.10.\n", + "[02-array-seq/lispy/py3.10/lis.py](02-array-seq/lispy/py3.10/lis.py)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [], + "metadata": { + "collapsed": false + } }, { "cell_type": "markdown", @@ -375,16 +1137,14 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 38, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[10, 20]" - ] + "text/plain": "[10, 20]" }, - "execution_count": 13, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -397,16 +1157,14 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 39, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[30, 40, 50, 60]" - ] + "text/plain": "[30, 40, 50, 60]" }, - "execution_count": 14, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -417,16 +1175,14 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 40, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[10, 20, 30]" - ] + "text/plain": "[10, 20, 30]" }, - "execution_count": 15, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -437,16 +1193,14 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 41, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[40, 50, 60]" - ] + "text/plain": "[40, 50, 60]" }, - "execution_count": 16, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } @@ -464,16 +1218,14 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 42, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "'bye'" - ] + "text/plain": "'bye'" }, - "execution_count": 17, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -485,16 +1237,14 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 43, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "'elcycib'" - ] + "text/plain": "'elcycib'" }, - "execution_count": 18, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -505,16 +1255,14 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 44, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "'eccb'" - ] + "text/plain": "'eccb'" }, - "execution_count": 19, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -527,12 +1275,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-9. Line items from a flat-file invoice" + "#### Example 2-13. Line items from a flat-file invoice" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 45, "metadata": {}, "outputs": [ { @@ -577,16 +1325,14 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 46, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" - ] + "text/plain": "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" }, - "execution_count": 21, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } @@ -598,16 +1344,14 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 47, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, 1, 20, 30, 5, 6, 7, 8, 9]" - ] + "text/plain": "[0, 1, 20, 30, 5, 6, 7, 8, 9]" }, - "execution_count": 22, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -619,16 +1363,14 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 48, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, 1, 20, 30, 5, 8, 9]" - ] + "text/plain": "[0, 1, 20, 30, 5, 8, 9]" }, - "execution_count": 23, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -640,16 +1382,14 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 49, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, 1, 20, 11, 5, 22, 9]" - ] + "text/plain": "[0, 1, 20, 11, 5, 22, 9]" }, - "execution_count": 24, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -668,7 +1408,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 50, "metadata": {}, "outputs": [ { @@ -688,16 +1428,14 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 51, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, 1, 100, 22, 9]" - ] + "text/plain": "[0, 1, 100, 22, 9]" }, - "execution_count": 26, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -711,21 +1449,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Using + and * with Sequences" + "## Using + and * with Sequences" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 52, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]" - ] + "text/plain": "[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]" }, - "execution_count": 27, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -737,16 +1473,14 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 53, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "'abcdabcdabcdabcdabcd'" - ] + "text/plain": "'abcdabcdabcdabcdabcd'" }, - "execution_count": 28, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -766,21 +1500,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-10. A list with three lists of length 3 can represent a tic-tac-toe board" + "#### Example 2-14. A list with three lists of length 3 can represent a tic-tac-toe board" ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 54, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" - ] + "text/plain": "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" }, - "execution_count": 29, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -792,16 +1524,14 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 55, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]" - ] + "text/plain": "[['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]" }, - "execution_count": 30, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -815,21 +1545,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-11. A list with three references to the same list is useless" + "#### Example 2-15. A list with three references to the same list is useless" ] }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 56, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" - ] + "text/plain": "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" }, - "execution_count": 31, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -841,16 +1569,14 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 57, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]" - ] + "text/plain": "[['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]" }, - "execution_count": 32, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -869,16 +1595,14 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 58, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" - ] + "text/plain": "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" }, - "execution_count": 33, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -893,16 +1617,14 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 59, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[['_', '_', '_'], ['_', '_', '_'], ['X', '_', '_']]" - ] + "text/plain": "[['_', '_', '_'], ['_', '_', '_'], ['X', '_', '_']]" }, - "execution_count": 34, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } @@ -921,7 +1643,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 60, "metadata": {}, "outputs": [], "source": [ @@ -931,16 +1653,14 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 61, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "4414271936" - ] + "text/plain": "140694277263808" }, - "execution_count": 36, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -952,16 +1672,14 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 62, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[1, 2, 3, 1, 2, 3]" - ] + "text/plain": "[1, 2, 3, 1, 2, 3]" }, - "execution_count": 37, + "execution_count": 62, "metadata": {}, "output_type": "execute_result" } @@ -973,16 +1691,14 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 63, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "True" - ] + "text/plain": "True" }, - "execution_count": 38, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } @@ -993,7 +1709,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 64, "metadata": {}, "outputs": [], "source": [ @@ -1003,16 +1719,14 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 65, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "4414275328" - ] + "text/plain": "140694329335488" }, - "execution_count": 40, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } @@ -1024,35 +1738,34 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 66, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "False" - ] + "text/plain": "False" }, - "execution_count": 41, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t *= 2\n", - "id(t) == idt # new tuple" + "id(t) == idt # new tuple" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### A += Assignment Puzzler" + "### A += Assignment Puzzler\n", + "#### Example 2-16. A riddle" ] }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 67, "metadata": {}, "outputs": [ { @@ -1071,18 +1784,28 @@ " print(repr(e))" ] }, + { + "cell_type": "markdown", + "source": [ + "#### Example 2-17. The unexpected result: item t2 is changed and an exception is raised" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 68, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "(1, 2, [30, 40, 50, 60])" - ] + "text/plain": "(1, 2, [30, 40, 50, 60])" }, - "execution_count": 43, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } @@ -1095,12 +1818,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-14. Bytecode for the expression s[a] += b" + "#### Example 2-18. Bytecode for the expression s[a] += b" ] }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 69, "metadata": {}, "outputs": [ { @@ -1135,16 +1858,14 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 70, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['apple', 'banana', 'grape', 'raspberry']" - ] + "text/plain": "['apple', 'banana', 'grape', 'raspberry']" }, - "execution_count": 45, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -1156,16 +1877,14 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 71, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['grape', 'raspberry', 'apple', 'banana']" - ] + "text/plain": "['grape', 'raspberry', 'apple', 'banana']" }, - "execution_count": 46, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } @@ -1176,16 +1895,14 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 72, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['raspberry', 'grape', 'banana', 'apple']" - ] + "text/plain": "['raspberry', 'grape', 'banana', 'apple']" }, - "execution_count": 47, + "execution_count": 72, "metadata": {}, "output_type": "execute_result" } @@ -1196,16 +1913,14 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 73, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['grape', 'apple', 'banana', 'raspberry']" - ] + "text/plain": "['grape', 'apple', 'banana', 'raspberry']" }, - "execution_count": 48, + "execution_count": 73, "metadata": {}, "output_type": "execute_result" } @@ -1216,16 +1931,14 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 74, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['raspberry', 'banana', 'grape', 'apple']" - ] + "text/plain": "['raspberry', 'banana', 'grape', 'apple']" }, - "execution_count": 49, + "execution_count": 74, "metadata": {}, "output_type": "execute_result" } @@ -1236,16 +1949,14 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 75, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['grape', 'raspberry', 'apple', 'banana']" - ] + "text/plain": "['grape', 'raspberry', 'apple', 'banana']" }, - "execution_count": 50, + "execution_count": 75, "metadata": {}, "output_type": "execute_result" } @@ -1256,16 +1967,14 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 76, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['apple', 'banana', 'grape', 'raspberry']" - ] + "text/plain": "['apple', 'banana', 'grape', 'raspberry']" }, - "execution_count": 51, + "execution_count": 76, "metadata": {}, "output_type": "execute_result" } @@ -1279,322 +1988,231 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Managing Ordered Sequences with bisect" + "## When a List Is Not the Answer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-15. bisect finds insertion points for items in a sorted sequence" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DEMO: bisect_right\n", - "haystack -> 1 4 5 6 8 12 15 20 21 23 23 26 29 30\n", - "31 @ 14 | | | | | | | | | | | | | |31\n", - "30 @ 14 | | | | | | | | | | | | | |30\n", - "29 @ 13 | | | | | | | | | | | | |29\n", - "23 @ 11 | | | | | | | | | | |23\n", - "22 @ 9 | | | | | | | | |22\n", - "10 @ 5 | | | | |10\n", - " 8 @ 5 | | | | |8 \n", - " 5 @ 3 | | |5 \n", - " 2 @ 1 |2 \n", - " 1 @ 1 |1 \n", - " 0 @ 0 0 \n" - ] - } - ], - "source": [ - "# BEGIN BISECT_DEMO\n", - "import bisect\n", - "import sys\n", - "\n", - "HAYSTACK = [1, 4, 5, 6, 8, 12, 15, 20, 21, 23, 23, 26, 29, 30]\n", - "NEEDLES = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31]\n", - "\n", - "ROW_FMT = '{0:2d} @ {1:2d} {2}{0:<2d}'\n", - "\n", - "def demo(haystack, needles, bisect_fn):\n", - " print('DEMO:', bisect_fn.__name__) # <1>\n", - " print('haystack ->', ' '.join('%2d' % n for n in haystack))\n", - " for needle in reversed(needles):\n", - " position = bisect_fn(haystack, needle) # <2>\n", - " offset = position * ' |' # <3>\n", - " print(ROW_FMT.format(needle, position, offset)) # <4>\n", - "\n", - "demo(HAYSTACK, NEEDLES, bisect.bisect) # <5>\n", - "# END BISECT_DEMO" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DEMO: bisect_left\n", - "haystack -> 1 4 5 6 8 12 15 20 21 23 23 26 29 30\n", - "31 @ 14 | | | | | | | | | | | | | |31\n", - "30 @ 13 | | | | | | | | | | | | |30\n", - "29 @ 12 | | | | | | | | | | | |29\n", - "23 @ 9 | | | | | | | | |23\n", - "22 @ 9 | | | | | | | | |22\n", - "10 @ 5 | | | | |10\n", - " 8 @ 4 | | | |8 \n", - " 5 @ 2 | |5 \n", - " 2 @ 1 |2 \n", - " 1 @ 0 1 \n", - " 0 @ 0 0 \n" - ] - } - ], - "source": [ - "demo(HAYSTACK, NEEDLES, bisect.bisect_left)" + "### Arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-16. Given a test score, grade returns the corresponding letter grade" + "#### Example 2-19. Creating, saving, and loading a large array of floats" ] }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 77, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['F', 'D', 'D', 'C', 'C', 'B', 'B', 'A', 'A']" - ] + "text/plain": "0.8190492979077034" }, - "execution_count": 54, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):\n", - " i = bisect.bisect(breakpoints, score)\n", - " return grades[i]\n", + "from array import array\n", + "from random import random, seed\n", + "seed(10) # Use seed to make the output consistent\n", "\n", - "[grade(score) for score in [55, 60, 65, 70, 75, 80, 85, 90, 95]]" + "floats = array('d', (random() for i in range(10 ** 7)))\n", + "floats[-1]" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 78, "metadata": {}, + "outputs": [], "source": [ - "#### Example 2-17. bisect_left maps a score of 60 to grade F, not D as in Example 2-16." + "with open('floats.bin', 'wb') as fp:\n", + " floats.tofile(fp)" ] }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 79, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['F', 'F', 'D', 'D', 'C', 'C', 'B', 'B', 'A']" - ] + "text/plain": "0.8190492979077034" }, - "execution_count": 55, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):\n", - " i = bisect.bisect_left(breakpoints, score)\n", - " return grades[i]\n", + "floats2 = array('d')\n", "\n", - "[grade(score) for score in [55, 60, 65, 70, 75, 80, 85, 90, 95]]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Example 2-18. Insort keeps a sorted sequence always sorted" + "with open('floats.bin', 'rb') as fp:\n", + " floats2.fromfile(fp, 10 ** 7)\n", + "\n", + "floats2[-1]" ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 80, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "insert 10 -> [10]\n", - "insert 0 -> [0, 10]\n", - "insert 6 -> [0, 6, 10]\n", - "insert 8 -> [0, 6, 8, 10]\n", - "insert 7 -> [0, 6, 7, 8, 10]\n", - "insert 2 -> [0, 2, 6, 7, 8, 10]\n", - "insert 10 -> [0, 2, 6, 7, 8, 10, 10]\n" - ] + "data": { + "text/plain": "True" + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "import bisect\n", - "import random\n", - "\n", - "SIZE = 7\n", - "\n", - "random.seed(1729)\n", - "\n", - "my_list = []\n", - "\n", - "for i in range(SIZE):\n", - " new_item = random.randrange(SIZE*2)\n", - " bisect.insort(my_list, new_item)\n", - " print(f'insert {new_item:2d} -> {my_list}')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## When a List Is Not the Answer" + "floats2 == floats" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Arrays" + "### Memory Views" ] }, { "cell_type": "markdown", - "metadata": {}, "source": [ - "#### Example 2-19. Creating, saving, and loading a large array of floats" - ] + "#### Example 2-20. Handling 6 bytes memory of as 1×6, 2×3, and 3×2 views" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } }, { "cell_type": "code", - "execution_count": 57, - "metadata": {}, + "execution_count": 81, "outputs": [ { "data": { - "text/plain": [ - "0.5963321947530882" - ] + "text/plain": "[0, 1, 2, 3, 4, 5]" }, - "execution_count": 57, + "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "from array import array\n", - "from random import random\n", - "\n", - "floats = array('d', (random() for i in range(10**7)))\n", - "floats[-1]" - ] + "octets = array('B', range(6))\n", + "m1 = memoryview(octets)\n", + "m1.tolist()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 58, - "metadata": {}, - "outputs": [], + "execution_count": 82, + "outputs": [ + { + "data": { + "text/plain": "[[0, 1, 2], [3, 4, 5]]" + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "with open('floats.bin', 'wb') as fp:\n", - " floats.tofile(fp)" - ] + "m2 = m1.cast('B', [2, 3])\n", + "m2.tolist()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 59, - "metadata": {}, + "execution_count": 83, "outputs": [ { "data": { - "text/plain": [ - "0.5963321947530882" - ] + "text/plain": "[[0, 1], [2, 3], [4, 5]]" }, - "execution_count": 59, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "floats2 = array('d')\n", - "\n", - "with open('floats.bin', 'rb') as fp:\n", - " floats2.fromfile(fp, 10**7)\n", - "\n", - "floats2[-1]" - ] + "m3 = m1.cast('B', [3, 2])\n", + "m3.tolist()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 60, - "metadata": {}, + "execution_count": 84, "outputs": [ { "data": { - "text/plain": [ - "True" - ] + "text/plain": "array('B', [0, 1, 2, 33, 22, 5])" }, - "execution_count": 60, + "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "floats2 == floats" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Memory Views" - ] + "m2[1,1] = 22\n", + "m3[1,1] = 33\n", + "octets" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-20. Changing the value of an array item by poking one of its bytes" + "#### Example 2-21. Changing the value of an 16-bit integer array item by poking one of its bytes" ] }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 85, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "5" - ] + "text/plain": "5" }, - "execution_count": 61, + "execution_count": 85, "metadata": {}, "output_type": "execute_result" } @@ -1607,16 +2225,14 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 86, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "-2" - ] + "text/plain": "-2" }, - "execution_count": 62, + "execution_count": 86, "metadata": {}, "output_type": "execute_result" } @@ -1627,16 +2243,14 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 87, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[254, 255, 255, 255, 0, 0, 1, 0, 2, 0]" - ] + "text/plain": "[254, 255, 255, 255, 0, 0, 1, 0, 2, 0]" }, - "execution_count": 63, + "execution_count": 87, "metadata": {}, "output_type": "execute_result" } @@ -1648,16 +2262,14 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 88, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array('h', [-2, -1, 1024, 1, 2])" - ] + "text/plain": "array('h', [-2, -1, 1024, 1, 2])" }, - "execution_count": 64, + "execution_count": 88, "metadata": {}, "output_type": "execute_result" } @@ -1671,50 +2283,47 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### NumPy and SciPy" + "### NumPy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-21. Basic operations with rows and columns in a numpy.ndarray" + "#### Example 2-22. Basic operations with rows and columns in a numpy.ndarray" ] }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 89, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])" - ] + "text/plain": "array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])" }, - "execution_count": 65, + "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", + "\n", "a = np.arange(12)\n", "a" ] }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 90, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "numpy.ndarray" - ] + "text/plain": "numpy.ndarray" }, - "execution_count": 66, + "execution_count": 90, "metadata": {}, "output_type": "execute_result" } @@ -1725,16 +2334,14 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 91, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "(12,)" - ] + "text/plain": "(12,)" }, - "execution_count": 67, + "execution_count": 91, "metadata": {}, "output_type": "execute_result" } @@ -1745,18 +2352,14 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 92, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([[ 0, 1, 2, 3],\n", - " [ 4, 5, 6, 7],\n", - " [ 8, 9, 10, 11]])" - ] + "text/plain": "array([[ 0, 1, 2, 3],\n [ 4, 5, 6, 7],\n [ 8, 9, 10, 11]])" }, - "execution_count": 68, + "execution_count": 92, "metadata": {}, "output_type": "execute_result" } @@ -1768,16 +2371,14 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 93, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([ 8, 9, 10, 11])" - ] + "text/plain": "array([ 8, 9, 10, 11])" }, - "execution_count": 69, + "execution_count": 93, "metadata": {}, "output_type": "execute_result" } @@ -1788,16 +2389,14 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 94, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "9" - ] + "text/plain": "9" }, - "execution_count": 70, + "execution_count": 94, "metadata": {}, "output_type": "execute_result" } @@ -1808,16 +2407,14 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 95, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([1, 5, 9])" - ] + "text/plain": "array([1, 5, 9])" }, - "execution_count": 71, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" } @@ -1828,25 +2425,20 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 96, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([[ 0, 4, 8],\n", - " [ 1, 5, 9],\n", - " [ 2, 6, 10],\n", - " [ 3, 7, 11]])" - ] + "text/plain": "array([[ 0, 4, 8],\n [ 1, 5, 9],\n [ 2, 6, 10],\n [ 3, 7, 11]])" }, - "execution_count": 72, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "a.transpose()\n" + "a.transpose()" ] }, { @@ -1858,7 +2450,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 97, "metadata": {}, "outputs": [], "source": [ @@ -1869,7 +2461,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 98, "metadata": {}, "outputs": [], "source": [ @@ -1878,16 +2470,14 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 99, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([0.29150425, 0.33893554, 0.08112756])" - ] + "text/plain": "array([0.06078257, 0.61741189, 0.84349987])" }, - "execution_count": 75, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -1898,16 +2488,14 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 100, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([0.14575213, 0.16946777, 0.04056378])" - ] + "text/plain": "array([0.03039128, 0.30870594, 0.42174994])" }, - "execution_count": 76, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -1919,16 +2507,14 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 101, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "True" - ] + "text/plain": "True" }, - "execution_count": 77, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } @@ -1943,7 +2529,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 102, "metadata": {}, "outputs": [], "source": [ @@ -1954,16 +2540,14 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 103, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "memmap([0.29150425, 0.33893554, 0.08112756])" - ] + "text/plain": "memmap([0.06078257, 0.61741189, 0.84349987])" }, - "execution_count": 79, + "execution_count": 103, "metadata": {}, "output_type": "execute_result" } @@ -1983,21 +2567,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-22. Working with a deque" + "#### Example 2-23. Working with a deque" ] }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 104, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" - ] + "text/plain": "deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" }, - "execution_count": 80, + "execution_count": 104, "metadata": {}, "output_type": "execute_result" } @@ -2011,16 +2593,14 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 105, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6])" - ] + "text/plain": "deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6])" }, - "execution_count": 81, + "execution_count": 105, "metadata": {}, "output_type": "execute_result" } @@ -2032,16 +2612,14 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 106, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])" - ] + "text/plain": "deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])" }, - "execution_count": 82, + "execution_count": 106, "metadata": {}, "output_type": "execute_result" } @@ -2053,16 +2631,14 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 107, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9])" - ] + "text/plain": "deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9])" }, - "execution_count": 83, + "execution_count": 107, "metadata": {}, "output_type": "execute_result" } @@ -2074,16 +2650,14 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 108, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33])" - ] + "text/plain": "deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33])" }, - "execution_count": 84, + "execution_count": 108, "metadata": {}, "output_type": "execute_result" } @@ -2095,16 +2669,14 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 109, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8])" - ] + "text/plain": "deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8])" }, - "execution_count": 85, + "execution_count": 109, "metadata": {}, "output_type": "execute_result" } @@ -2130,7 +2702,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 110, "metadata": {}, "outputs": [], "source": [ @@ -2139,7 +2711,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 111, "metadata": {}, "outputs": [ { @@ -2166,16 +2738,14 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 112, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, '1', 5, 6, '9', 14, 19, '23', 28, '28']" - ] + "text/plain": "[0, '1', 5, 6, '9', 14, 19, '23', 28, '28']" }, - "execution_count": 88, + "execution_count": 112, "metadata": {}, "output_type": "execute_result" } @@ -2188,16 +2758,14 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 113, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, '1', 14, 19, '23', 28, '28', 5, 6, '9']" - ] + "text/plain": "[0, '1', 14, 19, '23', 28, '28', 5, 6, '9']" }, - "execution_count": 89, + "execution_count": 113, "metadata": {}, "output_type": "execute_result" } @@ -2208,7 +2776,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 113, "metadata": {}, "outputs": [], "source": [] @@ -2235,4 +2803,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file From 434f8d26b6a11e6f59aba288933192af92f211ad Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Fri, 1 Apr 2022 17:23:12 -0300 Subject: [PATCH 103/127] covariance v. contravariance in Callable --- 08-def-type-hints/callable/variance.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 08-def-type-hints/callable/variance.py diff --git a/08-def-type-hints/callable/variance.py b/08-def-type-hints/callable/variance.py new file mode 100644 index 0000000..fcddbde --- /dev/null +++ b/08-def-type-hints/callable/variance.py @@ -0,0 +1,22 @@ +from collections.abc import Callable + +def update( # <1> + probe: Callable[[], float], # <2> + display: Callable[[float], None] # <3> + ) -> None: + temperature = probe() + # imagine lots of control code here + display(temperature) + +def probe_ok() -> int: # <4> + return 42 + +def display_wrong(temperature: int) -> None: # <5> + print(hex(temperature)) + +update(probe_ok, display_wrong) # type error # <6> + +def display_ok(temperature: complex) -> None: # <7> + print(temperature) + +update(probe_ok, display_ok) # OK # <8> From 08230737cc9b32a87510b517c448a2ca71a0fa18 Mon Sep 17 00:00:00 2001 From: Eduardo Scartezini Date: Fri, 8 Jul 2022 17:44:09 +0100 Subject: [PATCH 104/127] Fix chapter number to 2ed edition On 2ed the chapter "Object references, mutability and recycling" from 8 to 6 This commit fixes the readme with the new chapter number --- 06-obj-ref/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/06-obj-ref/README.rst b/06-obj-ref/README.rst index deac2fa..0d705e6 100644 --- a/06-obj-ref/README.rst +++ b/06-obj-ref/README.rst @@ -1,4 +1,4 @@ -Sample code for Chapter 8 - "Object references, mutability and recycling" +Sample code for Chapter 6 - "Object references, mutability and recycling" From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015) http://shop.oreilly.com/product/0636920032519.do From d00908d553a9746ea3c855c99c78a9d7308e1b23 Mon Sep 17 00:00:00 2001 From: Vlad Grin <67241634+grinvlad@users.noreply.github.com> Date: Sun, 31 Jul 2022 18:41:59 +0300 Subject: [PATCH 105/127] Fix filename in script --- 02-array-seq/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/02-array-seq/test.sh b/02-array-seq/test.sh index 4747320..906717d 100755 --- a/02-array-seq/test.sh +++ b/02-array-seq/test.sh @@ -1,4 +1,4 @@ #!/bin/bash python3 -m doctest bisect_demo.py -python3 -m doctest metro_lat_long.py +python3 -m doctest metro_lat_lon.py pytest -q --nbval From 3f9c89554aa5a14837e6f1977317b415ffab4ea1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Dec 2022 05:48:20 +0000 Subject: [PATCH 106/127] build(deps): bump certifi in /20-executors/getflags Bumps [certifi](https://github.com/certifi/python-certifi) from 2021.5.30 to 2022.12.7. - [Release notes](https://github.com/certifi/python-certifi/releases) - [Commits](https://github.com/certifi/python-certifi/compare/2021.05.30...2022.12.07) --- updated-dependencies: - dependency-name: certifi dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- 20-executors/getflags/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/20-executors/getflags/requirements.txt b/20-executors/getflags/requirements.txt index b8bb630..5dc5007 100644 --- a/20-executors/getflags/requirements.txt +++ b/20-executors/getflags/requirements.txt @@ -1,5 +1,5 @@ anyio==3.3.2 -certifi==2021.5.30 +certifi==2022.12.7 charset-normalizer==2.0.6 h11==0.12.0 httpcore==0.13.7 From d3309f03e88ce449c8f45beee25621221db86217 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Feb 2023 22:09:42 +0000 Subject: [PATCH 107/127] build(deps): bump starlette in /21-async/mojifinder Bumps [starlette](https://github.com/encode/starlette) from 0.13.6 to 0.25.0. - [Release notes](https://github.com/encode/starlette/releases) - [Changelog](https://github.com/encode/starlette/blob/master/docs/release-notes.md) - [Commits](https://github.com/encode/starlette/compare/0.13.6...0.25.0) --- updated-dependencies: - dependency-name: starlette dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- 21-async/mojifinder/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/21-async/mojifinder/requirements.txt b/21-async/mojifinder/requirements.txt index 1f7c3d6..4a1c97a 100644 --- a/21-async/mojifinder/requirements.txt +++ b/21-async/mojifinder/requirements.txt @@ -2,6 +2,6 @@ click==7.1.2 fastapi==0.65.2 h11==0.12.0 pydantic==1.8.2 -starlette==0.13.6 +starlette==0.25.0 typing-extensions==3.7.4.3 uvicorn==0.13.4 From 51055887dd6cca6c0b67e1f0072ec33d1eaa6351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20=C3=81lvarez=20Restrepo?= Date: Sun, 26 Mar 2023 17:28:08 -0500 Subject: [PATCH 108/127] Update flags2_asyncio.py Fix `error` variable not being reset when a successful coroutine call --- 20-executors/getflags/flags2_asyncio.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/20-executors/getflags/flags2_asyncio.py b/20-executors/getflags/flags2_asyncio.py index 4f1849a..b697840 100755 --- a/20-executors/getflags/flags2_asyncio.py +++ b/20-executors/getflags/flags2_asyncio.py @@ -79,6 +79,8 @@ async def supervisor(cc_list: list[str], error = exc # <10> except KeyboardInterrupt: break + else: + error = None if error: status = DownloadStatus.ERROR # <11> From 166a388ac84cbdea1c27404acfe1453c7bb6eb51 Mon Sep 17 00:00:00 2001 From: Mehdi Abbassi Date: Fri, 29 Sep 2023 07:46:37 +0330 Subject: [PATCH 109/127] using f-strings instead of % operator --- 01-data-model/data-model.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/01-data-model/data-model.ipynb b/01-data-model/data-model.ipynb index f525899..5b30397 100644 --- a/01-data-model/data-model.ipynb +++ b/01-data-model/data-model.ipynb @@ -482,7 +482,7 @@ " self.y = y\n", "\n", " def __repr__(self):\n", - " return 'Vector(%r, %r)' % (self.x, self.y)\n", + " return f'Vector({self.x!r}, {self.y!r})'\n", "\n", " def __abs__(self):\n", " return math.hypot(self.x, self.y)\n", From 567f3529e35fccc0087c9555f9fcd72c90043564 Mon Sep 17 00:00:00 2001 From: Tudor Capusan Date: Sun, 16 Jun 2024 11:37:29 -0400 Subject: [PATCH 110/127] fix spelling change from 'reminder' to 'remainder' to match intent --- 17-it-generator/columnize_iter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/17-it-generator/columnize_iter.py b/17-it-generator/columnize_iter.py index cee0f13..4362bc1 100644 --- a/17-it-generator/columnize_iter.py +++ b/17-it-generator/columnize_iter.py @@ -6,8 +6,8 @@ def columnize( ) -> Iterator[tuple[str, ...]]: # <1> if num_columns == 0: num_columns = round(len(sequence) ** 0.5) - num_rows, reminder = divmod(len(sequence), num_columns) - num_rows += bool(reminder) + num_rows, remainder = divmod(len(sequence), num_columns) + num_rows += bool(remainder) return (tuple(sequence[i::num_rows]) for i in range(num_rows)) # <2> # end::COLUMNIZE[] From 74ee1d4915d91b0af9013eb8fecf0302ca7f40c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 19:51:04 +0000 Subject: [PATCH 111/127] build(deps): bump idna from 3.2 to 3.7 in /20-executors/getflags Bumps [idna](https://github.com/kjd/idna) from 3.2 to 3.7. - [Release notes](https://github.com/kjd/idna/releases) - [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst) - [Commits](https://github.com/kjd/idna/compare/v3.2...v3.7) --- updated-dependencies: - dependency-name: idna dependency-version: '3.7' dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- 20-executors/getflags/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/20-executors/getflags/requirements.txt b/20-executors/getflags/requirements.txt index 5dc5007..c5fb66c 100644 --- a/20-executors/getflags/requirements.txt +++ b/20-executors/getflags/requirements.txt @@ -4,7 +4,7 @@ charset-normalizer==2.0.6 h11==0.12.0 httpcore==0.13.7 httpx==1.0.0b0 -idna==3.2 +idna==3.7 rfc3986==1.5.0 sniffio==1.2.0 tqdm==4.62.3 From 1b44dfaf11bf9bc8d87339f9fb8eddb294014f07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 19:51:09 +0000 Subject: [PATCH 112/127] build(deps): bump starlette in /21-async/mojifinder Bumps [starlette](https://github.com/encode/starlette) from 0.25.0 to 0.40.0. - [Release notes](https://github.com/encode/starlette/releases) - [Changelog](https://github.com/encode/starlette/blob/master/docs/release-notes.md) - [Commits](https://github.com/encode/starlette/compare/0.25.0...0.40.0) --- updated-dependencies: - dependency-name: starlette dependency-version: 0.40.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- 21-async/mojifinder/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/21-async/mojifinder/requirements.txt b/21-async/mojifinder/requirements.txt index 4a1c97a..72231bf 100644 --- a/21-async/mojifinder/requirements.txt +++ b/21-async/mojifinder/requirements.txt @@ -2,6 +2,6 @@ click==7.1.2 fastapi==0.65.2 h11==0.12.0 pydantic==1.8.2 -starlette==0.25.0 +starlette==0.40.0 typing-extensions==3.7.4.3 uvicorn==0.13.4 From d14c7bed18737930eebb0d398c609b4b9657753a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 19:51:11 +0000 Subject: [PATCH 113/127] build(deps): bump certifi in /20-executors/getflags Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.12.7 to 2024.7.4. - [Commits](https://github.com/certifi/python-certifi/compare/2022.12.07...2024.07.04) --- updated-dependencies: - dependency-name: certifi dependency-version: 2024.7.4 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- 20-executors/getflags/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/20-executors/getflags/requirements.txt b/20-executors/getflags/requirements.txt index 5dc5007..06da86c 100644 --- a/20-executors/getflags/requirements.txt +++ b/20-executors/getflags/requirements.txt @@ -1,5 +1,5 @@ anyio==3.3.2 -certifi==2022.12.7 +certifi==2024.7.4 charset-normalizer==2.0.6 h11==0.12.0 httpcore==0.13.7 From 8e911b19be0159c8d5ff6e36c56ddc7a799de788 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 19:51:51 +0000 Subject: [PATCH 114/127] build(deps): bump tqdm from 4.62.3 to 4.66.3 in /20-executors/getflags Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.62.3 to 4.66.3. - [Release notes](https://github.com/tqdm/tqdm/releases) - [Commits](https://github.com/tqdm/tqdm/compare/v4.62.3...v4.66.3) --- updated-dependencies: - dependency-name: tqdm dependency-version: 4.66.3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- 20-executors/getflags/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/20-executors/getflags/requirements.txt b/20-executors/getflags/requirements.txt index 5dc5007..5412046 100644 --- a/20-executors/getflags/requirements.txt +++ b/20-executors/getflags/requirements.txt @@ -7,4 +7,4 @@ httpx==1.0.0b0 idna==3.2 rfc3986==1.5.0 sniffio==1.2.0 -tqdm==4.62.3 +tqdm==4.66.3 From 68de220bd1950731b6cd50c0a0834647bbf6bd29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 19:51:51 +0000 Subject: [PATCH 115/127] build(deps): bump future in /08-def-type-hints/coordinates Bumps [future](https://github.com/PythonCharmers/python-future) from 0.18.2 to 0.18.3. - [Release notes](https://github.com/PythonCharmers/python-future/releases) - [Changelog](https://github.com/PythonCharmers/python-future/blob/master/docs/changelog.rst) - [Commits](https://github.com/PythonCharmers/python-future/compare/v0.18.2...v0.18.3) --- updated-dependencies: - dependency-name: future dependency-version: 0.18.3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- 08-def-type-hints/coordinates/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/08-def-type-hints/coordinates/requirements.txt b/08-def-type-hints/coordinates/requirements.txt index fb11094..7b7b424 100644 --- a/08-def-type-hints/coordinates/requirements.txt +++ b/08-def-type-hints/coordinates/requirements.txt @@ -1,2 +1,2 @@ geolib==1.0.7 -future==0.18.2 +future==0.18.3 From 74c52779b4fc88a217a96e0ec5a1c0029516096a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 19:53:20 +0000 Subject: [PATCH 116/127] build(deps): bump h11 from 0.12.0 to 0.16.0 in /20-executors/getflags Bumps [h11](https://github.com/python-hyper/h11) from 0.12.0 to 0.16.0. - [Commits](https://github.com/python-hyper/h11/compare/v0.12.0...v0.16.0) --- updated-dependencies: - dependency-name: h11 dependency-version: 0.16.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- 20-executors/getflags/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/20-executors/getflags/requirements.txt b/20-executors/getflags/requirements.txt index 464188b..6d74412 100644 --- a/20-executors/getflags/requirements.txt +++ b/20-executors/getflags/requirements.txt @@ -1,7 +1,7 @@ anyio==3.3.2 certifi==2024.7.4 charset-normalizer==2.0.6 -h11==0.12.0 +h11==0.16.0 httpcore==0.13.7 httpx==1.0.0b0 idna==3.2 From ab8a774924029233cfe1f6116d7c7a838c3485c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 19:55:09 +0000 Subject: [PATCH 117/127] build(deps): bump pydantic from 1.8.2 to 1.10.13 in /21-async/mojifinder Bumps [pydantic](https://github.com/pydantic/pydantic) from 1.8.2 to 1.10.13. - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md) - [Commits](https://github.com/pydantic/pydantic/compare/v1.8.2...v1.10.13) --- updated-dependencies: - dependency-name: pydantic dependency-version: 1.10.13 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- 21-async/mojifinder/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/21-async/mojifinder/requirements.txt b/21-async/mojifinder/requirements.txt index 72231bf..f7de911 100644 --- a/21-async/mojifinder/requirements.txt +++ b/21-async/mojifinder/requirements.txt @@ -1,7 +1,7 @@ click==7.1.2 fastapi==0.65.2 h11==0.12.0 -pydantic==1.8.2 +pydantic==1.10.13 starlette==0.40.0 typing-extensions==3.7.4.3 uvicorn==0.13.4 From 1bd4b1e7be896069225e72ac89303a26a8be46f3 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 21 May 2025 12:00:57 -0300 Subject: [PATCH 118/127] documentando FPY.LI.htacess --- links/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/links/README.md b/links/README.md index 6da595c..6c9cfa8 100644 --- a/links/README.md +++ b/links/README.md @@ -23,6 +23,7 @@ Please report broken links as bugs in the [`FPY.LI.htaccess`](FPY.LI.htaccess) f Also, feel free to send pull requests with fixes to that file. When I accept a PR, I will redeploy it to `fpy.li/.htaccess`. + ## Details Almost all URLs in the book are replaced with shortened versions like @@ -37,4 +38,8 @@ Exceptions: - URLs with `oreilly` in them are unchanged; - `fluentpython.com` URL (with no path) is unchanged; +The `custom.htacess` file contains the top redirects, which have custom names. +`FPY.LI.htaccess` has the same content, plus numbered URLs generated +from the links in each chapter in the book. + The `FPY.LI.htaccess` is deployed at the root folder in `http://fpy.li`. From 3d5588b75ee44ce650b82b87922bc40b6ea6615c Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 22 May 2025 01:11:34 -0300 Subject: [PATCH 119/127] gerador de URLs curtas --- links/FPY.LI.htaccess | 1074 +---------------------------------------- links/README.md | 15 +- links/custom.htaccess | 956 ++++++++++++++++++++++++++++++++++++ links/short.htaccess | 1 + links/short.py | 81 ++++ 5 files changed, 1051 insertions(+), 1076 deletions(-) create mode 100644 links/short.htaccess create mode 100755 links/short.py diff --git a/links/FPY.LI.htaccess b/links/FPY.LI.htaccess index cc779db..7338157 100644 --- a/links/FPY.LI.htaccess +++ b/links/FPY.LI.htaccess @@ -1,1072 +1,2 @@ -ErrorDocument 404 /404.html - -# main resources -RedirectTemp /book https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ -RedirectTemp /code https://github.com/fluentpython/example-code-2e -RedirectTemp /home https://www.fluentpython.com/ - -# URLs mentioned at least three times -RedirectTemp /bisect https://www.fluentpython.com/extra/ordered-sequences-with-bisect/ -RedirectTemp /cardxvi https://www.python.org/dev/peps/pep-0484/#the-numeric-tower -RedirectTemp /collec https://docs.python.org/3/library/collections.html -RedirectTemp /dask https://dask.org/ -RedirectTemp /dtmodel https://docs.python.org/3/reference/datamodel.html -RedirectTemp /descr101 https://www.python.org/download/releases/2.2.3/descrintro/ -RedirectTemp /descrhow https://docs.python.org/3/howto/descriptor.html -RedirectTemp /doctest https://docs.python.org/3/library/doctest.html -RedirectTemp /effectpy https://effectivepython.com/ -RedirectTemp /fmtspec https://docs.python.org/3/library/string.html#formatspec -RedirectTemp /gunicorn https://gunicorn.org/ -RedirectTemp /hashint https://www.fluentpython.com/extra/internals-of-sets-and-dicts/ -RedirectTemp /hattingh https://www.oreilly.com/library/view/using-asyncio-in/9781492075325/ -RedirectTemp /httpx https://www.python-httpx.org/ -RedirectTemp /initvar https://docs.python.org/3/library/dataclasses.html#init-only-variables -RedirectTemp /mypy https://mypy.readthedocs.io/en/stable/ -RedirectTemp /norvigdp http://norvig.com/design-patterns/ -RedirectTemp /nsphere https://en.wikipedia.org/wiki/N-sphere -RedirectTemp /oldcoro https://www.fluentpython.com/extra/classic-coroutines/ -RedirectTemp /pandas https://pandas.pydata.org/ -RedirectTemp /pycook3 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ -RedirectTemp /pynut3 https://www.oreilly.com/library/view/python-in-a/9781491913833/ -RedirectTemp /pypydif https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types -RedirectTemp /shed4051 https://github.com/python/typeshed/issues/4051 -RedirectTemp /specattr https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine -RedirectTemp /typing https://docs.python.org/3/library/typing.html -RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ - -# URL added during QA of the Second Edition -RedirectTemp /bdfl https://www.artima.com/weblogs/viewpost.jsp?thread=235725 - -# Python Enhancement Proposals -RedirectTemp /pep218 https://www.python.org/dev/peps/pep-0218/ -RedirectTemp /pep227 https://www.python.org/dev/peps/pep-0227/ -RedirectTemp /pep255 https://www.python.org/dev/peps/pep-0255/ -RedirectTemp /pep342 https://www.python.org/dev/peps/pep-0342/ -RedirectTemp /pep343 https://www.python.org/dev/peps/pep-0343/ -RedirectTemp /pep357 https://www.python.org/dev/peps/pep-0357/ -RedirectTemp /pep362 https://www.python.org/dev/peps/pep-0362/ -RedirectTemp /pep371 https://www.python.org/dev/peps/pep-0371/ -RedirectTemp /pep380 https://www.python.org/dev/peps/pep-0380/ -RedirectTemp /pep393 https://www.python.org/dev/peps/pep-0393/ -RedirectTemp /pep412 https://www.python.org/dev/peps/pep-0412/ -RedirectTemp /pep442 https://www.python.org/dev/peps/pep-0442/ -RedirectTemp /pep443 https://www.python.org/dev/peps/pep-0443/ -RedirectTemp /pep448 https://www.python.org/dev/peps/pep-0448/ -RedirectTemp /pep455 https://www.python.org/dev/peps/pep-0455/ -RedirectTemp /pep456 https://www.python.org/dev/peps/pep-0456/ -RedirectTemp /pep461 https://www.python.org/dev/peps/pep-0461/ -RedirectTemp /pep465 https://www.python.org/dev/peps/pep-0465/ -RedirectTemp /pep467 https://www.python.org/dev/peps/pep-0467/ -RedirectTemp /pep482 https://www.python.org/dev/peps/pep-0482/ -RedirectTemp /pep483 https://www.python.org/dev/peps/pep-0483/ -RedirectTemp /pep484 https://www.python.org/dev/peps/pep-0484/ -RedirectTemp /pep487 https://www.python.org/dev/peps/pep-0487/ -RedirectTemp /pep492 https://www.python.org/dev/peps/pep-0492/ -RedirectTemp /pep519 https://www.python.org/dev/peps/pep-0519/ -RedirectTemp /pep525 https://www.python.org/dev/peps/pep-0525/ -RedirectTemp /pep526 https://www.python.org/dev/peps/pep-0526/ -RedirectTemp /pep528 https://www.python.org/dev/peps/pep-0528/ -RedirectTemp /pep529 https://www.python.org/dev/peps/pep-0529/ -RedirectTemp /pep530 https://www.python.org/dev/peps/pep-0530/ -RedirectTemp /pep544 https://www.python.org/dev/peps/pep-0544/ -RedirectTemp /pep554 https://www.python.org/dev/peps/pep-0554/ -RedirectTemp /pep557 https://www.python.org/dev/peps/pep-0557/ -RedirectTemp /pep560 https://www.python.org/dev/peps/pep-0560/ -RedirectTemp /pep561 https://www.python.org/dev/peps/pep-0561/ -RedirectTemp /pep563 https://www.python.org/dev/peps/pep-0563/ -RedirectTemp /pep570 https://www.python.org/dev/peps/pep-0570/ -RedirectTemp /pep572 https://www.python.org/dev/peps/pep-0572/ -RedirectTemp /pep584 https://www.python.org/dev/peps/pep-0584/ -RedirectTemp /pep585 https://www.python.org/dev/peps/pep-0585/ -RedirectTemp /pep586 https://www.python.org/dev/peps/pep-0586/ -RedirectTemp /pep589 https://www.python.org/dev/peps/pep-0589/ -RedirectTemp /pep591 https://www.python.org/dev/peps/pep-0591/ -RedirectTemp /pep593 https://www.python.org/dev/peps/pep-0593/ -RedirectTemp /pep604 https://www.python.org/dev/peps/pep-0604/ -RedirectTemp /pep612 https://www.python.org/dev/peps/pep-0612/ -RedirectTemp /pep613 https://www.python.org/dev/peps/pep-0613/ -RedirectTemp /pep616 https://www.python.org/dev/peps/pep-0616/ -RedirectTemp /pep617 https://www.python.org/dev/peps/pep-0617/ -RedirectTemp /pep618 https://www.python.org/dev/peps/pep-0618/ -RedirectTemp /pep634 https://www.python.org/dev/peps/pep-0634/ -RedirectTemp /pep635 https://www.python.org/dev/peps/pep-0635/ -RedirectTemp /pep636 https://www.python.org/dev/peps/pep-0636/ -RedirectTemp /pep638 https://www.python.org/dev/peps/pep-0638/ -RedirectTemp /pep645 https://www.python.org/dev/peps/pep-0645/ -RedirectTemp /pep646 https://www.python.org/dev/peps/pep-0646/ -RedirectTemp /pep647 https://www.python.org/dev/peps/pep-0647/ -RedirectTemp /pep649 https://www.python.org/dev/peps/pep-0649/ -RedirectTemp /pep654 https://www.python.org/dev/peps/pep-0654/ -RedirectTemp /pep655 https://www.python.org/dev/peps/pep-0655/ -RedirectTemp /pep661 https://www.python.org/dev/peps/pep-0661/ -RedirectTemp /pep3099 https://www.python.org/dev/peps/pep-3099/ -RedirectTemp /pep3102 https://www.python.org/dev/peps/pep-3102/ -RedirectTemp /pep3104 https://www.python.org/dev/peps/pep-3104/ -RedirectTemp /pep3106 https://www.python.org/dev/peps/pep-3106/ -RedirectTemp /pep3107 https://www.python.org/dev/peps/pep-3107/ -RedirectTemp /pep3115 https://www.python.org/dev/peps/pep-3115/ -RedirectTemp /pep3118 https://www.python.org/dev/peps/pep-3118/ -RedirectTemp /pep3119 https://www.python.org/dev/peps/pep-3119/ -RedirectTemp /pep3129 https://www.python.org/dev/peps/pep-3129/ -RedirectTemp /pep3132 https://www.python.org/dev/peps/pep-3132/ -RedirectTemp /pep3141 https://www.python.org/dev/peps/pep-3141/ -RedirectTemp /pep3148 https://www.python.org/dev/peps/pep-3148/ -RedirectTemp /pep3155 https://www.python.org/dev/peps/pep-3155/ -RedirectTemp /pep3333 https://www.python.org/dev/peps/pep-3333/ - -# Remaining URLs by chapter - -############################################################ p -RedirectTemp /p-1 https://mail.python.org/pipermail/python-list/2002-December/134521.html -RedirectTemp /p-2 https://docs.python.org/3.10/tutorial/ -RedirectTemp /p-3 https://docs.python.org/3/tutorial/ -RedirectTemp /p-4 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ -RedirectTemp /p-5 https://www.oreilly.com/online-learning/try-now.html -RedirectTemp /p-6 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ -RedirectTemp /p-7 https://stackoverflow.com/users/95810/alex-martelli -RedirectTemp /p-8 https://pythonpro.com.br -RedirectTemp /p-9 https://groups.google.com/g/python-brasil -RedirectTemp /p-10 https://www.coffeelab.com.br/ -RedirectTemp /p-11 https://garoa.net.br/wiki/P%C3%A1gina_principal -############################################################ a -RedirectTemp /a-1 https://groups.google.com/forum/#!topic/python-tulip/Y4bhLNbKs74 -RedirectTemp /a-2 https://docs.python.org/3/library/asyncio-eventloop.html#executor -RedirectTemp /a-3 https://www.youtube.com/watch?v=x-kB2o8sd5c -RedirectTemp /a-4 https://www.youtube.com/watch?v=OSGv2VnC0go -RedirectTemp /a-5 https://mail.python.org/pipermail/python-ideas/2015-March/032557.html -RedirectTemp /a-6 https://pypi.org/project/pep8/ -RedirectTemp /a-7 https://pypi.org/project/flake8/ -RedirectTemp /a-8 https://pypi.org/project/pyflakes/ -RedirectTemp /a-9 https://pypi.org/project/mccabe/ -RedirectTemp /a-10 https://google.github.io/styleguide/pyguide.html -RedirectTemp /a-11 https://flask.palletsprojects.com/en/1.1.x/styleguide/ -RedirectTemp /a-12 https://docs.python-guide.org/ -RedirectTemp /a-13 https://david.goodger.org/projects/pycon/2007/idiomatic/handout.html -RedirectTemp /a-14 https://docs.mongodb.com/manual/about/#about-the-documentation-process -RedirectTemp /a-15 https://blog.startifact.com/posts/older/what-is-pythonic.html -RedirectTemp /a-16 https://mail.python.org/pipermail/tutor/2003-October/thread.html#25930 -RedirectTemp /a-17 https://mail.python.org/pipermail/python-list/2003-April/192027.html -RedirectTemp /a-18 https://www.python.org/doc/essays/ -############################################################ 01 -RedirectTemp /1-1 http://hugunin.net/story_of_jython.html -RedirectTemp /1-2 https://www.oreilly.com/library/view/jython-essentials/9781449397364/ -RedirectTemp /1-3 https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers -RedirectTemp /1-4 https://docs.python.org/3.10/library/string.html#format-string-syntax -RedirectTemp /1-5 https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr -RedirectTemp /1-6 https://docs.python.org/3/library/stdtypes.html#truth -RedirectTemp /1-7 https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists -RedirectTemp /1-8 https://www.python.org/doc/humor/#the-zen-of-python -RedirectTemp /1-9 https://stackoverflow.com/users/95810/alex-martelli -RedirectTemp /1-10 https://en.wikipedia.org/wiki/Object_model -RedirectTemp /1-11 https://www.dourish.com/goodies/jargon.html -RedirectTemp /1-12 https://zopeinterface.readthedocs.io/en/latest/ -RedirectTemp /1-13 https://plone.org/ -############################################################ 02 -RedirectTemp /2-1 https://github.com/fluentpython/example-code-2e/blob/master/02-array-seq/listcomp_speed.py -RedirectTemp /2-2 https://www.python.org/dev/peps/pep-3132/ -RedirectTemp /2-3 https://stackoverflow.com/questions/68630/are-tuples-more-efficient-than-lists-in-python/22140115#22140115 -RedirectTemp /2-4 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations -RedirectTemp /2-5 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations -RedirectTemp /2-6 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching -RedirectTemp /2-7 https://docs.python.org/3.10/whatsnew/3.10.html -RedirectTemp /2-8 https://en.wikipedia.org/wiki/Switch_statement#Fallthrough -RedirectTemp /2-9 https://en.wikipedia.org/wiki/Dangling_else -RedirectTemp /2-10 https://github.com/gvanrossum/patma/blob/3ece6444ef70122876fd9f0099eb9490a2d630df/EXAMPLES.md#case-6-a-very-deep-iterable-and-type-match-with-extraction -RedirectTemp /2-11 https://github.com/fluentpython/lispy/blob/main/original/norvig/lis.py -RedirectTemp /2-12 https://norvig.com/lispy.html -RedirectTemp /2-13 https://numpy.org/doc/stable/user/quickstart.html#indexing-slicing-and-iterating -RedirectTemp /2-14 https://pythontutor.com/ -RedirectTemp /2-15 https://en.wikipedia.org/wiki/Fluent_interface -RedirectTemp /2-16 https://docs.python.org/3/library/bisect.html#bisect.insort -RedirectTemp /2-17 https://stackoverflow.com/questions/4845418/when-should-a-memoryview-be-used/ -RedirectTemp /2-18 https://www.fluentpython.com/extra/parsing-binary-struct/ -RedirectTemp /2-19 http://www.netlib.org -RedirectTemp /2-20 https://pandas.pydata.org/ -RedirectTemp /2-21 https://scikit-learn.org/stable/ -RedirectTemp /2-22 https://docs.python.org/3/howto/sorting.html -RedirectTemp /2-23 https://www.python.org/dev/peps/pep-3132/ -RedirectTemp /2-24 https://bugs.python.org/issue2292 -RedirectTemp /2-25 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching -RedirectTemp /2-26 https://docs.python.org/3.10/whatsnew/3.10.html -RedirectTemp /2-27 https://www.python.org/dev/peps/pep-0636/#appendix-a-quick-intro -RedirectTemp /2-28 https://eli.thegreenplace.net/2011/11/28/less-copies-in-python-with-the-buffer-protocol-and-memoryviews/ -RedirectTemp /2-29 https://jakevdp.github.io/PythonDataScienceHandbook/ -RedirectTemp /2-30 https://www.oreilly.com/library/view/python-for-data/9781491957653/ -RedirectTemp /2-31 https://www.labri.fr/perso/nrougier/from-python-to-numpy/ -RedirectTemp /2-32 https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html -RedirectTemp /2-33 http://www.fonts101.com/fonts/view/Uncategorized/34398/Dijkstra -RedirectTemp /2-34 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types -RedirectTemp /2-35 https://en.wikipedia.org/wiki/Timsort -RedirectTemp /2-36 http://www.groklaw.net/pdf3/OraGoogle-1202.pdf -RedirectTemp /2-37 https://www.python.org/doc/humor/#id9 -############################################################ 03 -RedirectTemp /3-1 https://www.python.org/dev/peps/pep-0584/#motivation -RedirectTemp /3-2 https://docs.python.org/3.10/c-api/typeobj.html#Py_TPFLAGS_MAPPING -RedirectTemp /3-3 https://docs.python.org/3/glossary.html#term-hashable -RedirectTemp /3-4 https://docs.python.org/3/glossary.html#term-hashable -RedirectTemp /3-5 http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf -RedirectTemp /3-6 https://github.com/pingo-io/pingo-py -RedirectTemp /3-7 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/missing.py -RedirectTemp /3-8 https://docs.python.org/3/library/collections.html#collections.ChainMap -RedirectTemp /3-9 https://docs.python.org/3/library/collections.html#collections.Counter -RedirectTemp /3-10 https://docs.python.org/3/library/shelve.html -RedirectTemp /3-11 https://docs.python.org/3/library/dbm.html -RedirectTemp /3-12 https://docs.python.org/3/library/pickle.html -RedirectTemp /3-13 https://nedbatchelder.com/blog/202006/pickles_nine_flaws.html -RedirectTemp /3-14 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py#L813 -RedirectTemp /3-15 https://mail.python.org/pipermail/python-dev/2015-May/140003.html -RedirectTemp /3-16 https://bugs.python.org/issue18986 -RedirectTemp /3-17 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/transformdict.py -RedirectTemp /3-18 http://gandenberger.org/2018/03/10/ordered-dicts-vs-ordereddict/ -RedirectTemp /3-19 https://www.pypy.org/ -RedirectTemp /3-20 https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html -RedirectTemp /3-21 https://www.npopov.com/2014/12/22/PHPs-new-hashtable-implementation.html -RedirectTemp /3-22 https://www.youtube.com/watch?v=66P5FMkWoVU -RedirectTemp /3-23 https://pyvideo.org/video/276/the-mighty-dictionary-55/ -RedirectTemp /3-24 https://www.youtube.com/watch?v=p33CVV29OG8 -RedirectTemp /3-25 https://docs.python.org/3/whatsnew/3.6.html#new-dict-implementation -RedirectTemp /3-26 https://github.com/python/cpython/blob/cf7eaa4617295747ee5646c4e2b7e7a16d7c64ab/Objects/dictobject.c -RedirectTemp /3-27 https://github.com/python/cpython/blob/cf7eaa4617295747ee5646c4e2b7e7a16d7c64ab/Objects/dictnotes.txt -RedirectTemp /3-28 https://www.youtube.com/watch?v=tGAngdU_8D8 -RedirectTemp /3-29 https://speakerdeck.com/ramalho/python-set-practice-at-pycon -RedirectTemp /3-30 https://github.com/standupdev/uintset -RedirectTemp /3-31 https://spectrum.ieee.org/hans-peter-luhn-and-the-birth-of-the-hashing-algorithm -RedirectTemp /3-32 http://www.json.org/fatfree.html -RedirectTemp /3-33 https://twitter.com/mitsuhiko/status/1229385843585974272 -############################################################ 04 -RedirectTemp /4-1 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 -RedirectTemp /4-2 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ -RedirectTemp /4-3 https://www.fluentpython.com/extra/parsing-binary-struct/ -RedirectTemp /4-4 https://www.fluentpython.com/extra/multi-character-emojis/ -RedirectTemp /4-5 https://w3techs.com/technologies/overview/character_encoding -RedirectTemp /4-6 https://docs.python.org/3/library/codecs.html#codecs.register_error -RedirectTemp /4-7 https://docs.python.org/3/library/stdtypes.html#str.isascii -RedirectTemp /4-8 https://pypi.org/project/chardet/ -RedirectTemp /4-9 https://docs.python.org/3/library/codecs.html#encodings-and-unicode -RedirectTemp /4-10 https://nedbatchelder.com/text/unipain/unipain.html -RedirectTemp /4-11 https://devblogs.microsoft.com/commandline/windows-command-line-unicode-and-utf-8-output-text-buffer/ -RedirectTemp /4-12 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONIOENCODING -RedirectTemp /4-13 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONLEGACYWINDOWSSTDIO -RedirectTemp /4-14 https://docs.python.org/3/library/locale.html#locale.getpreferredencoding -RedirectTemp /4-15 http://www.w3.org/TR/charmod-norm/ -RedirectTemp /4-16 https://docs.python.org/3/library/locale.html?highlight=strxfrm#locale.strxfrm -RedirectTemp /4-17 https://pypi.org/project/pyuca/ -RedirectTemp /4-18 https://github.com/jtauber/pyuca -RedirectTemp /4-19 http://www.unicode.org/Public/UCA/6.3.0/allkeys.txt -RedirectTemp /4-20 https://pypi.org/project/PyICU/ -RedirectTemp /4-21 https://docs.python.org/3.10/library/stdtypes.html#str.isalpha -RedirectTemp /4-22 https://en.wikipedia.org/wiki/Unicode_character_property#General_Category -RedirectTemp /4-23 https://en.wikipedia.org/wiki/Unicode_character_property -RedirectTemp /4-24 https://github.com/microsoft/terminal -RedirectTemp /4-25 https://docs.python.org/3/library/unicodedata.html -RedirectTemp /4-26 https://docs.python.org/3/reference/lexical_analysis.html#string-literal-concatenation -RedirectTemp /4-27 https://docs.python.org/3/library/re.html -RedirectTemp /4-28 https://nedbatchelder.com/text/unipain.html -RedirectTemp /4-29 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 -RedirectTemp /4-30 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ -RedirectTemp /4-31 https://regebro.wordpress.com/2011/03/23/unconfusing-unicode-what-is-unicode/ -RedirectTemp /4-32 https://docs.python.org/3/howto/unicode.html -RedirectTemp /4-33 https://diveintopython3.net/strings.html -RedirectTemp /4-34 https://diveintopython3.net/ -RedirectTemp /4-35 https://finderiko.com/python-book -RedirectTemp /4-36 https://docs.python.org/3.0/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit -RedirectTemp /4-37 https://lucumr.pocoo.org/2013/7/2/the-updated-guide-to-unicode/ -RedirectTemp /4-38 http://python-notes.curiousefficiency.org/en/latest/python3/binary_protocols.html -RedirectTemp /4-39 http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html -RedirectTemp /4-40 https://docs.python.org/3/library/codecs.html#standard-encodings -RedirectTemp /4-41 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Tools/unicode/listcodecs.py -RedirectTemp /4-42 https://www.oreilly.com/library/view/unicode-explained/059610121X/ -RedirectTemp /4-43 https://www.informit.com/store/unicode-demystified-a-practical-programmers-guide-to-9780201700527 -RedirectTemp /4-44 https://unicodebook.readthedocs.io/index.html -RedirectTemp /4-45 https://www.w3.org/International/wiki/Case_folding -RedirectTemp /4-46 http://www.w3.org/TR/charmod-norm/ -RedirectTemp /4-47 http://unicode.org/reports/tr15/ -RedirectTemp /4-48 http://www.unicode.org/faq/normalization.html -RedirectTemp /4-49 http://www.unicode.org/ -RedirectTemp /4-50 http://www.macchiato.com/unicode/nfc-faq -RedirectTemp /4-51 https://stories.moma.org/the-original-emoji-set-has-been-added-to-the-museum-of-modern-arts-collection-c6060e141f61?gi=c403f5a840a5 -RedirectTemp /4-52 https://emojipedia.org/ -RedirectTemp /4-53 https://blog.emojipedia.org/correcting-the-record-on-the-first-emoji-set/ -RedirectTemp /4-54 http://emojitracker.com/ -RedirectTemp /4-55 http://www.unicode.org/glossary/#plain_text -RedirectTemp /4-56 http://www.methods.co.nz/asciidoc/ -RedirectTemp /4-57 https://atlas.oreilly.com/ -############################################################ 05 -RedirectTemp /5-1 https://docs.python.org/3/library/typing.html#typing.TypedDict -RedirectTemp /5-2 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations -RedirectTemp /5-3 https://docs.python.org/3/library/typing.html#typing.get_type_hints -RedirectTemp /5-4 https://docs.python.org/3.8/library/collections.html#collections.somenamedtuple._asdict -RedirectTemp /5-5 https://www.jetbrains.com/pycharm/ -RedirectTemp /5-6 https://www.python.org/dev/peps/pep-0484/#acceptable-type-hints -RedirectTemp /5-7 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass -RedirectTemp /5-8 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass -RedirectTemp /5-9 https://docs.python.org/3/library/dataclasses.html -RedirectTemp /5-10 https://docs.python.org/3/library/dataclasses.html#inheritance -RedirectTemp /5-11 https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations -RedirectTemp /5-12 https://dublincore.org/specifications/dublin-core/ -RedirectTemp /5-13 https://en.wikipedia.org/wiki/Dublin_Core -RedirectTemp /5-14 https://martinfowler.com/bliki/CodeSmell.html -RedirectTemp /5-15 https://martinfowler.com/books/refactoring.html -RedirectTemp /5-16 https://www.python.org/dev/peps/pep-0634/#class-patterns -RedirectTemp /5-17 https://docs.python.org/3/library/dataclasses.html -RedirectTemp /5-18 https://www.python.org/dev/peps/pep-0557/#id47 -RedirectTemp /5-19 https://www.python.org/dev/peps/pep-0557/#id48 -RedirectTemp /5-20 https://www.python.org/dev/peps/pep-0557/#id33 -RedirectTemp /5-21 https://realpython.com -RedirectTemp /5-22 https://realpython.com/python-data-classes/ -RedirectTemp /5-23 https://www.youtube.com/watch?v=T-TwcmT6Rcw -RedirectTemp /5-24 https://www.attrs.org/en/stable/ -RedirectTemp /5-25 https://glyph.twistedmatrix.com/2016/08/attrs.html -RedirectTemp /5-26 https://www.attrs.org/en/stable/why.html -RedirectTemp /5-27 https://github.com/dabeaz/cluegen -RedirectTemp /5-28 https://refactoring.guru/ -RedirectTemp /5-29 https://refactoring.guru/smells/data-class -RedirectTemp /5-30 https://web.archive.org/web/20190204130328/http://catb.org/esr/jargon/html/G/Guido.html -RedirectTemp /5-31 https://web.archive.org/web/20190211161610/http://catb.org/esr/jargon/html/index.html -RedirectTemp /5-32 https://www.attrs.org/en/stable/ -############################################################ 06 -RedirectTemp /6-1 https://www.olin.edu/faculty/profile/lynn-andrea-stein/ -RedirectTemp /6-2 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types -RedirectTemp /6-3 https://pythontutor.com/ -RedirectTemp /6-4 https://docs.python.org/3/library/copy.html -RedirectTemp /6-5 https://en.wikipedia.org/wiki/Principle_of_least_astonishment -RedirectTemp /6-6 https://docs.python.org/3/reference/datamodel.html#object.%5C_%5C_del__ -RedirectTemp /6-7 https://emptysqua.re/blog/pypy-garbage-collection-and-a-deadlock/ -RedirectTemp /6-8 https://www.youtube.com/watch?v=HHFCFJSPWrI&feature=youtu.be -RedirectTemp /6-9 http://pymotw.com/3/copy/ -RedirectTemp /6-10 http://pymotw.com/3/weakref/ -RedirectTemp /6-11 https://docs.python.org/3/library/gc.html -RedirectTemp /6-12 https://devguide.python.org/garbage_collector/ -RedirectTemp /6-13 https://devguide.python.org/ -RedirectTemp /6-14 https://www.python.org/dev/peps/pep-0442/ -RedirectTemp /6-15 https://en.wikipedia.org/wiki/String_interning -RedirectTemp /6-16 https://en.wikipedia.org/wiki/Haddocks%27_Eyes -RedirectTemp /6-17 https://thp.io/2012/python-gc/python_gc_final_2012-01-22.pdf -############################################################ 07 -RedirectTemp /7-1 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html -RedirectTemp /7-2 https://www.fluentpython.com/extra/function-introspection/ -RedirectTemp /7-3 https://docs.python.org/3/library/functions.html#map -RedirectTemp /7-4 https://en.wikipedia.org/wiki/Functional_programming -RedirectTemp /7-5 https://docs.python.org/3/howto/functional.html -RedirectTemp /7-6 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy -RedirectTemp /7-7 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters -RedirectTemp /7-8 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters -RedirectTemp /7-9 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/functools.py#L341 -RedirectTemp /7-10 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy -RedirectTemp /7-11 https://docs.python.org/3/howto/functional.html -RedirectTemp /7-12 https://stackoverflow.com/questions/3252228/python-why-is-functools-partial-necessary -RedirectTemp /7-13 https://speakerdeck.com/ramalho/beyond-paradigms-berlin-edition -RedirectTemp /7-14 https://www.youtube.com/watch?v=bF3a2VYXxa0 -RedirectTemp /7-15 http://cs.brown.edu/~sk/Publications/Papers/Published/sk-teach-pl-post-linnaean/ -RedirectTemp /7-16 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html -RedirectTemp /7-17 https://raw.githubusercontent.com/python/cpython/main/Misc/HISTORY -RedirectTemp /7-18 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html -############################################################ 08 -RedirectTemp /8-1 https://www.python.org/dev/peps/pep-0484/#non-goals -RedirectTemp /8-2 https://github.com/python/typing/issues/182 -RedirectTemp /8-3 https://github.com/python/mypy/issues/731 -RedirectTemp /8-4 https://github.com/google/pytype -RedirectTemp /8-5 https://github.com/Microsoft/pyright -RedirectTemp /8-6 https://pyre-check.org/ -RedirectTemp /8-7 https://mypy.readthedocs.io/en/stable/introduction.html -RedirectTemp /8-8 https://mypy.readthedocs.io/en/stable/config_file.html -RedirectTemp /8-9 https://pypi.org/project/flake8/ -RedirectTemp /8-10 https://pypi.org/project/blue/ -RedirectTemp /8-11 https://pypi.org/project/black/ -RedirectTemp /8-12 https://wefearchange.org/2020/11/steeringcouncil.rst.html -RedirectTemp /8-13 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes -RedirectTemp /8-14 https://en.wikipedia.org/wiki/Barbara_Liskov -RedirectTemp /8-15 https://en.wikipedia.org/wiki/Behavioral_subtyping -RedirectTemp /8-16 https://www.python.org/dev/peps/pep-0585/#implementation -RedirectTemp /8-17 https://docs.python.org/3/library/typing.html#module-contents -RedirectTemp /8-18 https://en.wikipedia.org/wiki/Geohash -RedirectTemp /8-19 https://en.wikipedia.org/wiki/Inverted_index -RedirectTemp /8-20 https://docs.python.org/3/library/typing.html#typing.List -RedirectTemp /8-21 https://docs.python.org/3/library/typing.html#typing.Dict -RedirectTemp /8-22 https://docs.python.org/3/library/typing.html#typing.Set -RedirectTemp /8-23 https://www.python.org/dev/peps/pep-0585/#implementation -RedirectTemp /8-24 https://docs.python.org/3/library/numbers.html -RedirectTemp /8-25 https://docs.python.org/3/library/typing.html#typing.List -RedirectTemp /8-26 https://github.com/python/typeshed -RedirectTemp /8-27 https://github.com/python/typeshed/blob/66cd36268a6a667714efaa27198a41d0d7f89477/stdlib/2and3/math.pyi#L45 -RedirectTemp /8-28 https://docs.python.org/3/library/statistics.html#statistics.mode -RedirectTemp /8-29 https://github.com/python/cpython/blob/822efa5695b5ba6c2316c1400e4e9ec2546f7ea5/Lib/statistics.py#L534 -RedirectTemp /8-30 https://github.com/python/typeshed/blob/e1e99245bb46223928eba68d4fc74962240ba5b4/stdlib/3/statistics.pyi -RedirectTemp /8-31 https://docs.python.org/3/library/statistics.html#statistics.mode -RedirectTemp /8-32 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/3/statistics.pyi#L32 -RedirectTemp /8-33 https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ -RedirectTemp /8-34 https://docs.python.org/3/library/typing.html#typing.Callable -RedirectTemp /8-35 https://pypi.org/project/blue/ -RedirectTemp /8-36 https://www.python.org/dev/peps/pep-0484/#id38 -RedirectTemp /8-37 https://docs.google.com/document/d/1aXs1tpwzPjW9MdsG5dI7clNFyYayFBkcXwRDo-qvbIk/preview -RedirectTemp /8-38 https://www.oreilly.com/library/view/the-best-software/9781590595008/ -RedirectTemp /8-39 https://www.youtube.com/watch?v=YFexUDjHO6w -RedirectTemp /8-40 https://www.youtube.com/watch?v=YFexUDjHO6w&t=13m40s -RedirectTemp /8-41 https://bernat.tech/posts/the-state-of-type-hints-in-python/ -RedirectTemp /8-42 https://realpython.com/python-type-checking/ -RedirectTemp /8-43 https://cjolowicz.github.io/posts/hypermodern-python-04-typing/ -RedirectTemp /8-44 https://mypy.readthedocs.io/en/stable/index.html -RedirectTemp /8-45 https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html -RedirectTemp /8-46 https://mypy.readthedocs.io/en/stable/common_issues.html -RedirectTemp /8-47 https://github.com/typeddjango/awesome-python-typing -RedirectTemp /8-48 https://docs.python.org/3/library/functions.html#max -RedirectTemp /8-49 https://en.wikipedia.org/wiki/Linguistic_relativity -RedirectTemp /8-50 https://pypistats.org/top -RedirectTemp /8-51 https://github.com/psf/requests/issues/3855 -RedirectTemp /8-52 https://lwn.net/Articles/643399/ -RedirectTemp /8-53 https://docs.python-requests.org/en/master/api/#requests.request -RedirectTemp /8-54 https://queue.acm.org/detail.cfm?id=1039523 -############################################################ 09 -RedirectTemp /9-1 https://docs.python.org/3/library/dis.html -RedirectTemp /9-2 https://en.wikipedia.org/wiki/Memoization -RedirectTemp /9-3 https://numpy.org/doc/stable/user/basics.types.html -RedirectTemp /9-4 https://docs.python.org/3/library/functools.html#functools.singledispatch -RedirectTemp /9-5 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/README.md -RedirectTemp /9-6 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/01-how-you-implemented-your-python-decorator-is-wrong.md -RedirectTemp /9-7 https://wrapt.readthedocs.io/en/latest/ -RedirectTemp /9-8 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch09.html -RedirectTemp /9-9 https://pypi.org/project/decorator/ -RedirectTemp /9-10 https://wiki.python.org/moin/PythonDecoratorLibrary -RedirectTemp /9-11 http://web.archive.org/web/20201109032203/http://effbot.org/zone/closure.htm -RedirectTemp /9-12 https://www.python.org/dev/peps/pep-3104/ -RedirectTemp /9-13 https://www.python.org/dev/peps/pep-0227/ -RedirectTemp /9-14 https://www.python.org/dev/peps/pep-0443/ -RedirectTemp /9-15 https://www.artima.com/weblogs/viewpost.jsp?thread=101605 -RedirectTemp /9-16 https://reg.readthedocs.io/en/latest/ -RedirectTemp /9-17 https://morepath.readthedocs.io/en/latest/ -RedirectTemp /9-18 https://www.gnu.org/software/emacs/manual/html_node/elisp/Dynamic-Binding.html -RedirectTemp /9-19 http://www.paulgraham.com/rootsoflisp.html -RedirectTemp /9-20 http://www-formal.stanford.edu/jmc/recursive/recursive.html -RedirectTemp /9-21 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this -############################################################ 10 -RedirectTemp /10-1 https://en.wikipedia.org/wiki/Software_design_pattern -RedirectTemp /10-2 https://en.wikipedia.org/wiki/Iterator_pattern -RedirectTemp /10-3 https://github.com/python/mypy/issues/9397 -RedirectTemp /10-4 http://www.norvig.com/design-patterns/index.htm -RedirectTemp /10-5 https://pyvideo.org/video/1110/python-design-patterns/ -RedirectTemp /10-6 http://www.aleax.it/gdd_pydp.pdf -RedirectTemp /10-7 https://perl.plover.com/yak/design/ -RedirectTemp /10-8 https://en.wikipedia.org/wiki/Turtles_all_the_way_down -############################################################ 11 -RedirectTemp /11-1 https://blog.startifact.com/posts/older/what-is-pythonic.html -RedirectTemp /11-2 https://julien.danjou.info/guide-python-static-class-abstract-methods/ -RedirectTemp /11-3 https://docs.python.org/3/library/string.html#formatspec -RedirectTemp /11-4 https://docs.python.org/3/reference/lexical_analysis.html#f-strings -RedirectTemp /11-5 https://docs.python.org/3/library/string.html#format-string-syntax -RedirectTemp /11-6 https://docs.python.org/3/library/string.html#formatspec -RedirectTemp /11-7 https://docs.python.org/3/reference/datamodel.html#object.__hash__ -RedirectTemp /11-8 https://web.archive.org/web/20161025185040/http://pythonpaste.org/StyleGuide.html -RedirectTemp /11-9 https://docs.python.org/3/tutorial/modules.html#more-on-modules -RedirectTemp /11-10 https://docs.python.org/3/library/gettext.html#gettext.NullTranslations -RedirectTemp /11-11 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/mem_test.py -RedirectTemp /11-12 https://docs.python.org/3/reference/datamodel.html#basic-customization -RedirectTemp /11-13 http://esug.org/data/HistoricalDocuments/TheSmalltalkReport/ST07/04wo.pdf -RedirectTemp /11-14 https://www.artima.com/articles/the-simplest-thing-that-could-possibly-work#part3 -RedirectTemp /11-15 https://docs.oracle.com/javase/tutorial/essential/environment/security.html -RedirectTemp /11-16 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/private/Expose.java -RedirectTemp /11-17 https://docs.oracle.com/javase/tutorial/essential/environment/security.html -############################################################ 12 -RedirectTemp /12-1 https://en.wikipedia.org/wiki/Vector_space_model -RedirectTemp /12-2 https://pypi.org/project/gensim/ -RedirectTemp /12-3 https://docs.python.org/3/library/functions.html#enumerate -RedirectTemp /12-4 https://mathworld.wolfram.com/Hypersphere.html -RedirectTemp /12-5 https://en.wikipedia.org/wiki/Fold_(higher-order_function) -RedirectTemp /12-6 https://docs.python.org/2.5/whatsnew/pep-357.html -RedirectTemp /12-7 https://docs.python.org/3/reference/datamodel.html#special-method-names -RedirectTemp /12-8 https://en.wikipedia.org/wiki/KISS_principle -RedirectTemp /12-9 https://mail.python.org/pipermail/python-list/2000-July/046184.html -RedirectTemp /12-10 https://en.wikipedia.org/wiki/Duck_typing -RedirectTemp /12-11 https://mail.python.org/mailman/listinfo/python-list -RedirectTemp /12-12 https://mail.python.org/pipermail/python-list/2003-April/218568.html -############################################################ 13 -RedirectTemp /13-1 https://docs.python.org/3/c-api/index.html -RedirectTemp /13-2 https://docs.python.org/3/c-api/sequence.html -RedirectTemp /13-3 https://github.com/python/cpython/blob/31ceccb2c77854893f3a754aca04bedd74bedb10/Lib/_collections_abc.py#L870 -RedirectTemp /13-4 https://en.wikipedia.org/wiki/Monkey_patch -RedirectTemp /13-5 https://www.gevent.org/api/gevent.monkey.html -RedirectTemp /13-6 https://docs.python.org/3/library/random.html#random.shuffle -RedirectTemp /13-7 https://docs.python.org/3/reference/datamodel.html#emulating-container-types -RedirectTemp /13-8 https://docs.python.org/3/library/collections.html#collections.namedtuple -RedirectTemp /13-9 https://github.com/python/typeshed/blob/24afb531ffd07083d6a74be917342195062f7277/stdlib/collections/__init__.pyi -RedirectTemp /13-10 https://docs.python.org/3/glossary.html#term-abstract-base-class -RedirectTemp /13-11 https://en.wikipedia.org/wiki/Duck_typing#History -RedirectTemp /13-12 http://ptgmedia.pearsoncmg.com/images/020163371x/items/item33.html -RedirectTemp /13-13 https://docs.python.org/3/library/bisect.html#bisect.bisect -RedirectTemp /13-14 https://github.com/python/cpython/blob/main/Lib/_collections_abc.py -RedirectTemp /13-15 https://github.com/python/cpython/blob/main/Lib/abc.py -RedirectTemp /13-16 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes -RedirectTemp /13-17 https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable -RedirectTemp /13-18 https://docs.python.org/3/library/abc.html -RedirectTemp /13-19 https://docs.python.org/dev/library/abc.html#abc.abstractmethod -RedirectTemp /13-20 https://docs.python.org/dev/library/abc.html -RedirectTemp /13-21 https://docs.python.org/3/library/os.html#os.urandom -RedirectTemp /13-22 https://github.com/python/mypy/issues/2922 -RedirectTemp /13-23 https://docs.python.org/3/library/stdtypes.html#truth -RedirectTemp /13-24 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py -RedirectTemp /13-25 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L369 -RedirectTemp /13-26 https://github.com/python/cpython/blob/c0a9afe2ac1820409e6173bd1893ebee2cf50270/Lib/abc.py#L196 -RedirectTemp /13-27 https://bugs.python.org/issue31333 -RedirectTemp /13-28 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Modules/_abc.c#L605 -RedirectTemp /13-29 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L881 -RedirectTemp /13-30 https://docs.python.org/3/library/typing.html#protocols -RedirectTemp /13-31 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Lib/typing.py#L1751 -RedirectTemp /13-32 https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ -RedirectTemp /13-33 https://martinfowler.com/bliki/RoleInterface.html -RedirectTemp /13-34 https://en.wikipedia.org/wiki/Interface_segregation_principle -RedirectTemp /13-35 https://github.com/python/typeshed/blob/master/CONTRIBUTING.md -RedirectTemp /13-36 https://gist.github.com/asukakenji/ac8a05644a2e98f1d5ea8c299541fce9 -RedirectTemp /13-37 https://www.python.org/dev/peps/pep-0544/#runtime-checkable-decorator-and-narrowing-types-by-isinstance -RedirectTemp /13-38 https://www.python.org/dev/peps/pep-0544/#merging-and-extending-protocols -RedirectTemp /13-39 https://numpy.org/devdocs/user/basics.types.html -RedirectTemp /13-40 https://github.com/python/typeshed/blob/master/stdlib/statistics.pyi -RedirectTemp /13-41 https://bugs.python.org/issue41974 -RedirectTemp /13-42 https://glyph.twistedmatrix.com/2020/07/new-duck.html -RedirectTemp /13-43 https://glyph.twistedmatrix.com/2021/03/interfaces-and-protocols.html -RedirectTemp /13-44 https://plone.org/ -RedirectTemp /13-45 https://trypyramid.com/ -RedirectTemp /13-46 https://twistedmatrix.com/trac/ -RedirectTemp /13-47 https://www.artima.com/articles/contracts-in-python -RedirectTemp /13-48 https://martinfowler.com/bliki/DynamicTyping.html -RedirectTemp /13-49 https://martinfowler.com/bliki/RoleInterface.html -RedirectTemp /13-50 https://mypy.readthedocs.io/en/stable/protocols.html -RedirectTemp /13-51 https://pymotw.com/3/abc/index.html -RedirectTemp /13-52 https://www.python.org/dev/peps/pep-3119/ -RedirectTemp /13-53 https://www.python.org/dev/peps/pep-3141/ -RedirectTemp /13-54 https://docs.python.org/3/library/numbers.html -RedirectTemp /13-55 https://github.com/python/mypy/issues/3186 -RedirectTemp /13-56 https://stackoverflow.com/questions/69334475/how-to-hint-at-number-types-i-e-subclasses-of-number-not-numbers-themselv/69383462#69383462 -RedirectTemp /13-57 https://github.com/python/mypy/issues/3186 -RedirectTemp /13-58 https://martinfowler.com/articles/lean-inception/ -RedirectTemp /13-59 https://martinfowler.com -RedirectTemp /13-60 https://www.jetbrains.com/pycharm/ -RedirectTemp /13-61 https://wingware.com/ -RedirectTemp /13-62 https://code.visualstudio.com/ -############################################################ 14 -RedirectTemp /14-1 http://worrydream.com/EarlyHistoryOfSmalltalk/ -RedirectTemp /14-2 https://docs.python.org/3/tutorial/classes.html -RedirectTemp /14-3 https://docs.python.org/3/library/collections.html#ordereddict-examples-and-recipes -RedirectTemp /14-4 https://discuss.python.org/t/is-it-time-to-deprecate-unbound-super-methods/1833 -RedirectTemp /14-5 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types -RedirectTemp /14-6 https://docs.python.org/3/library/collections.html -RedirectTemp /14-7 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/strkeydict_dictsub.py -RedirectTemp /14-8 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types -RedirectTemp /14-9 https://en.wikipedia.org/wiki/Breadth-first_search -RedirectTemp /14-10 https://www.python.org/download/releases/2.3/mro/ -RedirectTemp /14-11 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/uppermixin.py -RedirectTemp /14-12 https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html -RedirectTemp /14-13 https://docs.python.org/3/library/collections.abc.html -RedirectTemp /14-14 https://github.com/python/cpython/blob/8ece98a7e418c3c68a4c61bc47a2d0931b59a889/Lib/collections/__init__.py#L1084 -RedirectTemp /14-15 https://docs.python.org/3/library/http.server.html -RedirectTemp /14-16 https://github.com/python/cpython/blob/17c23167942498296f0bdfffe52e72d53d66d693/Lib/http/server.py#L144 -RedirectTemp /14-17 https://github.com/python/cpython/blob/699ee016af5736ffc80f68359617611a22b72943/Lib/socketserver.py#L664 -RedirectTemp /14-18 https://docs.python.org/3/library/socketserver.html#socketserver.ForkingMixIn -RedirectTemp /14-19 https://docs.python.org/3/library/os.html#os.fork -RedirectTemp /14-20 https://en.wikipedia.org/wiki/POSIX -RedirectTemp /14-21 http://ccbv.co.uk/ -RedirectTemp /14-22 https://github.com/django/django/tree/main/django/views/generic -RedirectTemp /14-23 https://en.wikipedia.org/wiki/Template_method_pattern -RedirectTemp /14-24 https://docs.python.org/3/library/tkinter.html -RedirectTemp /14-25 https://docs.python.org/3/library/tkinter.ttk.html -RedirectTemp /14-26 https://docs.oracle.com/javase/10/docs/api/java/awt/package-tree.html -RedirectTemp /14-27 https://docs.oracle.com/javase/10/docs/api/javax/swing/package-tree.html -RedirectTemp /14-28 https://squeak.org/ -RedirectTemp /14-29 https://github.com/django/django/blob/b64db05b9cedd96905d637a2d824cbbf428e40e7/django/views/generic/list.py#L194 -RedirectTemp /14-30 https://github.com/python/cpython/blob/8ed183391241f0c73e7ba7f42b1d49fc02985f7b/Lib/tkinter/__init__.py#L2618 -RedirectTemp /14-31 https://docs.python.org/3/library/socketserver.html -RedirectTemp /14-32 https://docs.python.org/3/library/socketserver.html#socketserver.BaseServer -RedirectTemp /14-33 https://github.com/python/cpython/blob/699ee016af5736ffc80f68359617611a22b72943/Lib/socketserver.py#L153 -RedirectTemp /14-34 https://docs.python.org/3/library/typing.html#typing.final -RedirectTemp /14-35 https://docs.python.org/3/library/typing.html#typing.Final -RedirectTemp /14-36 https://docs.python.org/3/library/collections.abc.html -RedirectTemp /14-37 https://hynek.me/articles/python-subclassing-redux/ -RedirectTemp /14-38 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch08.html#super -RedirectTemp /14-39 https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ -RedirectTemp /14-40 https://fuhm.net/super-harmful/ -RedirectTemp /14-41 https://stackoverflow.com/questions/30190185/how-to-use-super-with-one-argument/30190341#30190341 -RedirectTemp /14-42 https://www.artima.com/weblogs/viewpost.jsp?thread=246488 -RedirectTemp /14-43 https://www.artima.com/weblogs/viewpost.jsp?thread=281127 -RedirectTemp /14-44 https://www.artima.com/weblogs/viewpost.jsp?thread=246341 -RedirectTemp /14-45 https://www.artima.com/weblogs/viewpost.jsp?thread=246483 -RedirectTemp /14-46 https://www.artima.com/weblogs/viewpost.jsp?thread=236275 -RedirectTemp /14-47 https://www.artima.com/weblogs/viewpost.jsp?thread=236278 -RedirectTemp /14-48 https://www.artima.com/weblogs/viewpost.jsp?thread=237121 -RedirectTemp /14-49 https://python-patterns.guide/gang-of-four/composition-over-inheritance/ -RedirectTemp /14-50 https://python-patterns.guide/ -RedirectTemp /14-51 https://www.youtube.com/watch?v=3MNVP9-hglc -RedirectTemp /14-52 http://worrydream.com/EarlyHistoryOfSmalltalk/ -RedirectTemp /14-53 https://en.wikipedia.org/wiki/Polymorphism_(computer_science) -############################################################ 15 -RedirectTemp /15-1 https://www.youtube.com/watch?v=csL8DLXGNlU&t=92m5s -RedirectTemp /15-2 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/2and3/builtins.pyi#L1434 -RedirectTemp /15-3 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/2and3/builtins.pyi -RedirectTemp /15-4 https://twitter.com/gwidion/status/1265384692464967680 -RedirectTemp /15-5 https://pypi.org/project/pydantic/ -RedirectTemp /15-6 https://google.github.io/pytype/faq.html -RedirectTemp /15-7 https://google.github.io/pytype/faq.html -RedirectTemp /15-8 https://lxml.de/ -RedirectTemp /15-9 https://docs.python.org/3/library/xml.etree.elementtree.html -RedirectTemp /15-10 https://mypy.readthedocs.io/en/stable/common_issues.html -RedirectTemp /15-11 https://mypy.readthedocs.io/en/stable/common_issues.html#types-of-empty-collections -RedirectTemp /15-12 https://github.com/python/typing/issues/182 -RedirectTemp /15-13 https://pypi.org/project/pydantic/ -RedirectTemp /15-14 https://mypy.readthedocs.io/en/stable/type_narrowing.html#casts -RedirectTemp /15-15 https://github.com/python/cpython/blob/bee66d3cb98e740f9d8057eb7f503122052ca5d8/Lib/typing.py#L1340 -RedirectTemp /15-16 https://www.python.org/dev/peps/pep-0484/#casts -RedirectTemp /15-17 https://github.com/python/typeshed/issues/5535 -RedirectTemp /15-18 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams -RedirectTemp /15-19 https://github.com/python/cpython/blob/b798ab06937f8bb24b444a49dd42e11fff15e654/Lib/test/test_asyncio/test_server.py#L55 -RedirectTemp /15-20 https://en.wikipedia.org/wiki/Code_smell -RedirectTemp /15-21 https://mail.python.org/archives/list/typing-sig@python.org/message/5LCWMN2UY2UQNLC5Z47GHBZKSPZW4I63/ -RedirectTemp /15-22 https://mypy.readthedocs.io/en/stable/error_codes.html#error-codes -RedirectTemp /15-23 https://github.com/fluentpython/example-code-2e/blob/master/15-more-types/clip_annot.py -RedirectTemp /15-24 https://docs.python.org/3/library/typing.html#introspection-helpers -RedirectTemp /15-25 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations -RedirectTemp /15-26 https://www.python.org/dev/peps/pep-0563/#abstract -RedirectTemp /15-27 https://mail.python.org/archives/list/python-dev@python.org/message/ZBJ7MD6CSGM6LZAOTET7GXAVBZB7O77O/ -RedirectTemp /15-28 https://docs.python.org/3.10/howto/annotations.html -RedirectTemp /15-29 https://docs.python.org/3/library/typing.html#user-defined-generic-types -RedirectTemp /15-30 https://github.com/python/typeshed/blob/bfc83c365a0b26ab16586beac77ff16729d0e473/stdlib/builtins.pyi#L743 -RedirectTemp /15-31 https://docs.python.org/3.10/library/typing.html#typing.FrozenSet -RedirectTemp /15-32 https://docs.python.org/3.10/library/typing.html#typing.Generator -RedirectTemp /15-33 https://docs.python.org/3.10/library/typing.html#typing.AsyncGenerator -RedirectTemp /15-34 https://github.com/python/cpython/blob/46b16d0bdbb1722daed10389e27226a2370f1635/Lib/typing.py#L1786 -RedirectTemp /15-35 https://github.com/python/typeshed/blob/2a9f081abbf01134e4e04ced6a750107db904d70/stdlib/builtins.pyi#L239 -RedirectTemp /15-36 https://www.oreilly.com/library/view/robust-python/9781098100650/ -RedirectTemp /15-37 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance -RedirectTemp /15-38 https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generic-types -RedirectTemp /15-39 https://mypy.readthedocs.io/en/stable/common_issues.html#variance -RedirectTemp /15-40 https://www.artima.com/weblogs/viewpost.jsp?thread=85551 -RedirectTemp /15-41 https://dl.acm.org/action/cookieAbsent -RedirectTemp /15-42 http://bracha.org/pluggableTypesPosition.pdf -RedirectTemp /15-43 https://www.researchgate.net/publication/213886116_Static_Typing_Where_Possible_Dynamic_Typing_When_Needed_The_End_of_the_Cold_War_Between_Programming_Languages -RedirectTemp /15-44 https://www.atomickotlin.com/atomickotlin/ -RedirectTemp /15-45 https://www.informit.com/store/effective-java-9780134685991 -RedirectTemp /15-46 https://www.manning.com/books/programming-with-types -RedirectTemp /15-47 https://www.oreilly.com/library/view/programming-typescript/9781492037644/ -RedirectTemp /15-48 https://www.informit.com/store/dart-programming-language-9780321927705 -RedirectTemp /15-49 https://www.yodaiken.com/2017/09/15/bad-ideas-in-type-theory/ -RedirectTemp /15-50 https://www.yodaiken.com/2017/11/30/types-considered-harmful-ii/ -RedirectTemp /15-51 https://web.archive.org/web/20071010002142/http://weblogs.java.net/blog/arnold/archive/2005/06/generics_consid_1.html -RedirectTemp /15-52 https://github.com/python/cpython/blob/3e7ee02327db13e4337374597cdc4458ecb9e3ad/Lib/asyncio/trsock.py#L5 -RedirectTemp /15-53 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance -############################################################ 16 -RedirectTemp /16-1 http://www.gotw.ca/publications/c_family_interview.htm -RedirectTemp /16-2 https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations -RedirectTemp /16-3 https://docs.python.org/3/reference/datamodel.html#object.__neg__ -RedirectTemp /16-4 https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#boolean-indexing -RedirectTemp /16-5 https://docs.python.org/3/library/collections.html#collections.Counter -RedirectTemp /16-6 https://docs.python.org/3/reference/datamodel.html#emulating-container-types -RedirectTemp /16-7 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations -RedirectTemp /16-8 https://www.fluentpython.com/lingo/#fail-fast -RedirectTemp /16-9 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Objects/typeobject.c#L4598 -RedirectTemp /16-10 https://neopythonic.blogspot.com/2019/03/why-operators-are-useful.html -RedirectTemp /16-11 https://treyhunner.com/2019/03/python-deep-comparisons-and-code-readability/ -RedirectTemp /16-12 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations -RedirectTemp /16-13 https://docs.python.org/3/library/pathlib.html -RedirectTemp /16-14 https://pypi.org/project/scapy/ -RedirectTemp /16-15 https://scapy.readthedocs.io/en/latest/usage.html#stacking-layers -RedirectTemp /16-16 https://docs.python.org/3/library/functools.html#functools.total_ordering -RedirectTemp /16-17 https://wiki.illinois.edu//wiki/download/attachments/273416327/ingalls.pdf -RedirectTemp /16-18 https://wiki.illinois.edu//wiki/download/attachments/273416327/double-dispatch.pdf -RedirectTemp /16-19 http://www.gotw.ca/publications/c_family_interview.htm -RedirectTemp /16-20 http://www.gotw.ca/publications/c_family_interview.htm -RedirectTemp /16-21 https://doc.rust-lang.org/std/ops/index.html -RedirectTemp /16-22 https://www.fluentpython.com/lingo/#lazy -############################################################ 17 -RedirectTemp /17-1 http://www.paulgraham.com/icad.html -RedirectTemp /17-2 https://en.wikipedia.org/wiki/Sentinel_value -RedirectTemp /17-3 https://docs.python.org/3.10/library/functions.html#iter -RedirectTemp /17-4 https://docs.python.org/3.10/library/functions.html#iter -RedirectTemp /17-5 https://github.com/python/cpython/blob/b1930bf75f276cd7ca08c4455298128d89adf7d1/Lib/_collections_abc.py#L271 -RedirectTemp /17-6 https://github.com/python/cpython/blob/main/Lib/types.py#L6 -RedirectTemp /17-7 https://en.wikipedia.org/wiki/CLU_(programming_language) -RedirectTemp /17-8 https://docs.python.org/3/glossary.html -RedirectTemp /17-9 https://docs.python.org/3/glossary.html#term-generator-iterator -RedirectTemp /17-10 https://docs.python.org/3/glossary.html#term-generator-expression -RedirectTemp /17-11 https://marc.info/?l=python-list&m=141826925106951&w=2 -RedirectTemp /17-12 https://docs.python.org/3/library/os.html#os.walk -RedirectTemp /17-13 https://docs.python.org/3/library/itertools.html -RedirectTemp /17-14 https://docs.python.org/3/library/exceptions.html#exception-hierarchy -RedirectTemp /17-15 https://en.wikipedia.org/wiki/Depth-first_search -RedirectTemp /17-16 https://docs.python.org/3.10/library/typing.html#typing.TypeAlias -RedirectTemp /17-17 https://docs.python.org/3/library/typing.html#typing.Generator -RedirectTemp /17-18 http://www.dabeaz.com/coroutines/Coroutines.pdf -RedirectTemp /17-19 http://www.dabeaz.com/coroutines/Coroutines.pdf -RedirectTemp /17-20 https://mail.python.org/pipermail/python-ideas/2009-April/003841.html -RedirectTemp /17-21 https://mail.python.org/pipermail/python-ideas/2009-April/003912.html -RedirectTemp /17-22 https://docs.python.org/3/library/exceptions.html#StopIteration -RedirectTemp /17-23 https://docs.python.org/3/reference/expressions.html#yield-expressions -RedirectTemp /17-24 https://docs.python.org/3/reference/index.html -RedirectTemp /17-25 https://github.com/python/cpython/blob/6f743e7a4da904f61dfa84cc7d7385e4dcc79ac5/Lib/typing.py#L2060 -RedirectTemp /17-26 http://catb.org/~esr/jargon/html/G/grok.html -RedirectTemp /17-27 https://docs.python.org/3/reference/expressions.html#yieldexpr -RedirectTemp /17-28 https://docs.python.org/3/library/itertools.html -RedirectTemp /17-29 https://docs.python.org/3/library/itertools.html#itertools-recipes -RedirectTemp /17-30 https://more-itertools.readthedocs.io/en/stable/index.html -RedirectTemp /17-31 https://rittau.org/2006/11/java-iterators-are-not-iterable/ -RedirectTemp /17-32 https://docs.python.org/3/whatsnew/3.3.html#pep-380-syntax-for-delegating-to-a-subgenerator -RedirectTemp /17-33 http://www.dabeaz.com/generators/ -RedirectTemp /17-34 http://www.dabeaz.com/coroutines/ -RedirectTemp /17-35 https://archive.org/details/pyvideo_213___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-1-of-3 -RedirectTemp /17-36 https://archive.org/details/pyvideo_215___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-2-of-3 -RedirectTemp /17-37 https://archive.org/details/pyvideo_214___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-3-of-3 -RedirectTemp /17-38 http://www.dabeaz.com/finalgenerator/ -RedirectTemp /17-39 https://web.archive.org/web/20200218150637/http://seriously.dontusethiscode.com/2013/05/01/greedy-coroutine.html -RedirectTemp /17-40 https://effectivepython.com/ -RedirectTemp /17-41 https://effectivepython.com/2015/03/10/consider-coroutines-to-run-many-functions-concurrently -RedirectTemp /17-42 https://en.wikipedia.org/wiki/Conway's_Game_of_Life -RedirectTemp /17-43 https://gist.github.com/ramalho/da5590bc38c973408839 -RedirectTemp /17-44 https://gist.github.com/ramalho/da5590bc38c973408839 -RedirectTemp /17-45 https://journal.code4lib.org/articles/4893 -RedirectTemp /17-46 https://github.com/fluentpython/isis2json -RedirectTemp /17-47 https://github.com/fluentpython/isis2json/blob/master/README.rst -############################################################ 18 -RedirectTemp /18-1 https://pyvideo.org/video/1669/keynote-3/ -RedirectTemp /18-2 https://docs.python.org/3/library/sqlite3.html#using-the-connection-as-a-context-manager -RedirectTemp /18-3 https://docs.python.org/3/library/threading.html#using-locks-conditions-and-semaphores-in-the-with-statement -RedirectTemp /18-4 https://docs.python.org/3/library/decimal.html#decimal.localcontext -RedirectTemp /18-5 https://docs.python.org/3/library/unittest.mock.html#patch -RedirectTemp /18-6 https://docs.python.org/3/library/contextlib.html#contextlib.redirect_stdout -RedirectTemp /18-7 https://docs.python.org/3/library/sys.html#sys.exc_info -RedirectTemp /18-8 https://en.wikipedia.org/wiki/LL_parser -RedirectTemp /18-9 https://docs.python.org/3/library/contextlib.html -RedirectTemp /18-10 https://github.com/python/cpython/blob/8afab2ebbc1b343cd88d058914cf622fe687a2be/Lib/contextlib.py#L123 -RedirectTemp /18-11 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ -RedirectTemp /18-12 https://docs.python.org/3/library/fileinput.html#fileinput.input -RedirectTemp /18-13 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ -RedirectTemp /18-14 https://en.wikipedia.org/wiki/Euclidean_algorithm -RedirectTemp /18-15 https://github.com/fluentpython/example-code-2e/tree/master/18-with-match/lispy/py3.10/ -RedirectTemp /18-16 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lispy.py -RedirectTemp /18-17 https://github.com/fluentpython/example-code-2e/blob/6527037ae7319ba370a1ee2d9fe79214d0ed9452/18-with-match/lispy/py3.10/lis.py#L35 -RedirectTemp /18-18 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/examples_test.py -RedirectTemp /18-19 https://github.com/python/typeshed/issues/6042 -RedirectTemp /18-20 https://github.com/fluentpython/lispy/tree/main/mylis -RedirectTemp /18-21 https://github.com/fluentpython/example-code-2e/blob/00e4741926e1b771ee7c753148b1415c0bd12e39/02-array-seq/lispy/py3.10/examples_test.py -RedirectTemp /18-22 https://mitpress.mit.edu/sites/default/files/sicp/index.html -RedirectTemp /18-23 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/examples_test.py -RedirectTemp /18-24 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/lis.py -RedirectTemp /18-25 https://www.python.org/dev/peps/pep-0634/#or-patterns -RedirectTemp /18-26 https://en.wikipedia.org/wiki/Lambda#Character_encodings -RedirectTemp /18-27 https://docs.python.org/3/reference/compound_stmts.html -RedirectTemp /18-28 https://docs.python.org/3/glossary.html#term-eafp -RedirectTemp /18-29 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 -RedirectTemp /18-30 https://docs.python.org/3/reference/compound_stmts.html -RedirectTemp /18-31 https://stackoverflow.com/questions/16138232/is-it-a-good-practice-to-use-try-except-else-in-python -RedirectTemp /18-32 https://docs.python.org/3/library/stdtypes.html#typecontextmanager -RedirectTemp /18-33 https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers -RedirectTemp /18-34 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 -RedirectTemp /18-35 https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger-1?slide=34 -RedirectTemp /18-36 https://preshing.com/20110920/the-python-with-statement-by-example/ -RedirectTemp /18-37 https://www.rath.org/on-the-beauty-of-pythons-exitstack.html -RedirectTemp /18-38 https://norvig.com/lispy.html -RedirectTemp /18-39 https://norvig.com/lispy2.html -RedirectTemp /18-40 https://github.com/norvig/pytudes -RedirectTemp /18-41 https://github.com/fluentpython/lispy -RedirectTemp /18-42 https://racket-lang.org/ -RedirectTemp /18-43 https://pyvideo.org/video/1669/keynote-3/ -RedirectTemp /18-44 https://en.wikipedia.org/wiki/Tail_call -RedirectTemp /18-45 https://2ality.com/2015/06/tail-call-optimization.html -RedirectTemp /18-46 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lis.py -RedirectTemp /18-47 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lispy.py -RedirectTemp /18-48 http://neopythonic.blogspot.com/2009/04/final-words-on-tail-calls.html -RedirectTemp /18-49 https://webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit/ -RedirectTemp /18-50 http://kangax.github.io/compat-table/es6/ -RedirectTemp /18-51 https://world.hey.com/mgmarlow/what-happened-to-proper-tail-calls-in-javascript-5494c256 -RedirectTemp /18-52 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html -RedirectTemp /18-53 https://github.com/fluentpython/lispy/blob/main/mylis/mylis_2/lis.py -RedirectTemp /18-54 https://github.com/fluentpython/lispy/tree/main/mylis -############################################################ 19 -RedirectTemp /19-1 https://go.dev/blog/waza-talk -RedirectTemp /19-2 https://en.wikipedia.org/wiki/Graphics_processing_unit -RedirectTemp /19-3 https://docs.python.org/3/library/sys.html#sys.getswitchinterval -RedirectTemp /19-4 https://docs.python.org/3/library/sys.html#sys.setswitchinterval -RedirectTemp /19-5 https://en.wikipedia.org/wiki/System_call -RedirectTemp /19-6 https://mail.python.org/pipermail/python-dev/2009-October/093356.html -RedirectTemp /19-7 http://www.dabeaz.com/finalgenerator/ -RedirectTemp /19-8 https://docs.python.org/3/library/threading.html#thread-objects -RedirectTemp /19-9 https://www.pypy.org/ -RedirectTemp /19-10 https://mail.python.org/pipermail/python-list/2009-February/675659.html -RedirectTemp /19-11 https://en.wikipedia.org/wiki/Braille_Patterns -RedirectTemp /19-12 https://docs.python.org/3/library/multiprocessing.shared_memory.html -RedirectTemp /19-13 https://docs.python.org/3/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.ShareableList -RedirectTemp /19-14 https://greenlet.readthedocs.io/en/latest/ -RedirectTemp /19-15 https://docs.sqlalchemy.org/en/14/changelog/migration_14.html#asynchronous-io-support-for-core-and-orm -RedirectTemp /19-16 https://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html -RedirectTemp /19-17 http://www.gevent.org/ -RedirectTemp /19-18 https://github.com/gevent/gevent/wiki/Projects -RedirectTemp /19-19 https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor-example -RedirectTemp /19-20 https://github.com/python/asyncio/issues/284 -RedirectTemp /19-21 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor -RedirectTemp /19-22 https://mail.python.org/archives/list/python-dev@python.org/message/JBYXQH3NV3YBF7P2HLHB5CD6V3GVTY55/ -RedirectTemp /19-23 https://docs.python.org/3/library/queue.html#queue.SimpleQueue.get -RedirectTemp /19-24 https://en.wikipedia.org/wiki/Race_condition -RedirectTemp /19-25 https://github.com/fluentpython/example-code-2e/commit/2c1230579db99738a5e5e6802063bda585f6476d -RedirectTemp /19-26 https://github.com/fluentpython/example-code-2e/blob/master/19-concurrency/primes/README.md -RedirectTemp /19-27 https://github.com/fluentpython/example-code-2e/blob/master/19-concurrency/primes/threads.py -RedirectTemp /19-28 https://en.wikipedia.org/wiki/Context_switch -RedirectTemp /19-29 http://www.gotw.ca/publications/concurrency-ddj.htm -RedirectTemp /19-30 https://www.ansible.com/ -RedirectTemp /19-31 https://saltproject.io/ -RedirectTemp /19-32 https://www.fabfile.org/ -RedirectTemp /19-33 https://engineering.fb.com/2016/05/27/production-engineering/python-in-production-engineering/ -RedirectTemp /19-34 https://jupyter.org/ -RedirectTemp /19-35 https://docs.bokeh.org/en/latest/index.html -RedirectTemp /19-36 https://www.tensorflow.org/ -RedirectTemp /19-37 https://pytorch.org/ -RedirectTemp /19-38 https://www.oreilly.com/radar/where-programming-ops-ai-and-the-cloud-are-headed-in-2021/ -RedirectTemp /19-39 https://www.youtube.com/watch?v=ods97a5Pzw0 -RedirectTemp /19-40 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy -RedirectTemp /19-41 https://modwsgi.readthedocs.io/en/master/ -RedirectTemp /19-42 https://uwsgi-docs.readthedocs.io/en/latest/ -RedirectTemp /19-43 https://unit.nginx.org/ -RedirectTemp /19-44 https://www.techatbloomberg.com/blog/configuring-uwsgi-production-deployment/ -RedirectTemp /19-45 https://www.youtube.com/watch?v=p6R1h2Nn468 -RedirectTemp /19-46 https://asgi.readthedocs.io/en/latest/index.html -RedirectTemp /19-47 https://docs.celeryproject.org/en/stable/getting-started/introduction.html -RedirectTemp /19-48 https://python-rq.org/ -RedirectTemp /19-49 https://docs.celeryproject.org/en/stable/faq.html#what-kinds-of-things-should-i-use-celery-for -RedirectTemp /19-50 https://redis.io/ -RedirectTemp /19-51 https://realpython.com/intro-to-python-threading/ -RedirectTemp /19-52 https://pymotw.com/3/concurrency.html -RedirectTemp /19-53 https://www.pearson.com/us/higher-education/program/Hellmann-Python-3-Standard-Library-by-Example-The/PGM328871.html -RedirectTemp /19-54 https://docs.python.org/3/library/multiprocessing.html#programming-guidelines -RedirectTemp /19-55 https://docs.python.org/3/library/multiprocessing.html -RedirectTemp /19-56 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ -RedirectTemp /19-57 https://link.springer.com/book/10.1007/978-1-4842-5793-7?error=cookies_not_supported&code=2ed5d61d-ae9f-4f3d-94ac-0f68cf45ea4f -RedirectTemp /19-58 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 -RedirectTemp /19-59 https://greenteapress.com/wp/semaphores/ -RedirectTemp /19-60 https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock -RedirectTemp /19-61 https://docs.python.org/3/faq/library.html#can-t-we-get-rid-of-the-global-interpreter-lock -RedirectTemp /19-62 https://www.artima.com/weblogs/viewpost.jsp?thread=214235 -RedirectTemp /19-63 http://jessenoller.com/blog/2009/02/01/python-threads-and-the-global-interpreter-lock -RedirectTemp /19-64 https://realpython.com/products/cpython-internals-book/ -RedirectTemp /19-65 http://www.dabeaz.com/GIL/ -RedirectTemp /19-66 http://www.dabeaz.com/python/UnderstandingGIL.pdf -RedirectTemp /19-67 https://bugs.python.org/issue7946#msg223110 -RedirectTemp /19-68 https://bugs.python.org/issue7946 -RedirectTemp /19-69 https://www.fullstackpython.com/ -RedirectTemp /19-70 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ -RedirectTemp /19-71 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 -RedirectTemp /19-72 https://www.packtpub.com/product/distributed-computing-with-python/9781785889691 -RedirectTemp /19-73 https://towardsdatascience.com/python-performance-and-gpus-1be860ffd58d?gi=a7d537cc2fb4 -RedirectTemp /19-74 https://instagram-engineering.com/web-service-efficiency-at-instagram-with-python-4976d078e366?gi=12a441991c88 -RedirectTemp /19-75 https://www.oreilly.com/library/view/architecture-patterns-with/9781492052197/ -RedirectTemp /19-76 https://www.cosmicpython.com/ -RedirectTemp /19-77 https://pypi.org/project/lelo/ -RedirectTemp /19-78 https://github.com/npryce/python-parallelize -RedirectTemp /19-79 https://github.com/ericsnowcurrently/multi-core-python/wiki -RedirectTemp /19-80 https://gist.github.com/markshannon/79cace3656b40e21b7021504daee950c -RedirectTemp /19-81 https://mail.python.org/archives/list/python-dev@python.org/message/YOOQZCFOKEPQ24YHWWLQSJ3RCXFMS7D7/ -RedirectTemp /19-82 https://en.wikipedia.org/wiki/Communicating_sequential_processes -RedirectTemp /19-83 https://github.com/stackless-dev/stackless/wiki -RedirectTemp /19-84 https://www.eveonline.com -RedirectTemp /19-85 https://www.ccpgames.com/ -RedirectTemp /19-86 https://stackless.readthedocs.io/en/3.6-slp/stackless-python.html#history -RedirectTemp /19-87 https://doc.pypy.org/en/latest/stackless.html -RedirectTemp /19-88 https://greenlet.readthedocs.io/en/latest/ -RedirectTemp /19-89 http://www.gevent.org/ -RedirectTemp /19-90 http://thespianpy.com/doc/ -RedirectTemp /19-91 https://pykka.readthedocs.io/en/latest/ -RedirectTemp /19-92 https://www.manning.com/books/rabbitmq-in-action -RedirectTemp /19-93 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ -RedirectTemp /19-94 https://en.wikipedia.org/wiki/OpenCL -RedirectTemp /19-95 https://media.pragprog.com/titles/pb7con/Bonus_Chapter.pdf -RedirectTemp /19-96 https://martinfowler.com/ -RedirectTemp /19-97 https://martinfowler.com/articles/patterns-of-distributed-systems/ -RedirectTemp /19-98 https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/ -RedirectTemp /19-99 https://www.oreilly.com/library/view/oscon-2016-video/9781491965153/video247021.html -RedirectTemp /19-100 https://www.oreilly.com/library/view/designing-for-scalability/9781449361556/ -RedirectTemp /19-101 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy -RedirectTemp /19-102 https://en.wikipedia.org/wiki/KISS_principle -RedirectTemp /19-103 https://www.usenix.org/conference/hotos15/workshop-program/presentation/mcsherry -############################################################ 20 -RedirectTemp /20-1 https://www.artima.com/weblogs/viewpost.jsp?thread=299551 -RedirectTemp /20-2 https://docs.python.org/3/library/http.server.html -RedirectTemp /20-3 https://www.youtube.com/watch?v=A9e9Cy1UkME -RedirectTemp /20-4 https://www.cia.gov/the-world-factbook/ -RedirectTemp /20-5 https://docs.python-requests.org/en/latest/ -RedirectTemp /20-6 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor -RedirectTemp /20-7 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed -RedirectTemp /20-8 https://docs.python.org/3/library/concurrent.futures.html -RedirectTemp /20-9 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.Executor -RedirectTemp /20-10 https://github.com/fluentpython/example-code-2e/blob/master/20-executors/getflags/flags2_common.py -RedirectTemp /20-11 https://github.com/noamraph/tqdm -RedirectTemp /20-12 https://www.youtube.com/watch?v=M8Z65tAl5l4 -RedirectTemp /20-13 https://github.com/noamraph/tqdm/blob/master/README.md -RedirectTemp /20-14 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed -RedirectTemp /20-15 https://docs.python.org/3/library/asyncio-task.html#asyncio.as_completed -RedirectTemp /20-16 https://www.cloudflare.com/ -RedirectTemp /20-17 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags -RedirectTemp /20-18 https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418 -RedirectTemp /20-19 https://en.wikipedia.org/wiki/Embarrassingly_parallel -RedirectTemp /20-20 https://pyvideo.org/video/480/pyconau-2010--the-future-is-soon/ -RedirectTemp /20-21 http://www.dabeaz.com/coroutines/ -RedirectTemp /20-22 https://en.wikipedia.org/wiki/POSIX_Threads -RedirectTemp /20-23 https://en.wikipedia.org/wiki/C_dynamic_memory_allocation -RedirectTemp /20-24 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ -RedirectTemp /20-25 https://hexdocs.pm/ecto/getting-started.html -############################################################ 21 -RedirectTemp /21-1 https://docs.python.org/3/library/asyncio.html -RedirectTemp /21-2 https://bugs.python.org/issue43216 -RedirectTemp /21-3 https://bugs.python.org/issue36921 -RedirectTemp /21-4 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.getaddrinfo -RedirectTemp /21-5 https://docs.python.org/3/library/socket.html#socket.getaddrinfo -RedirectTemp /21-6 https://docs.python.org/3.10/library/asyncio-eventloop.html#asyncio.get_event_loop -RedirectTemp /21-7 https://www.python.org/dev/peps/pep-0492/#await-expression -RedirectTemp /21-8 https://www.fluentpython.com/extra/classic-coroutines/#yield_from_meaning_sec -RedirectTemp /21-9 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags -RedirectTemp /21-10 https://magicstack.github.io/asyncpg/current/ -RedirectTemp /21-11 https://magicstack.github.io/asyncpg/current/api/index.html#transactions -RedirectTemp /21-12 https://magicstack.github.io/asyncpg/current/api/index.html#transactions -RedirectTemp /21-13 https://github.com/MagicStack/asyncpg/blob/4d39a05268ce4cc01b00458223a767542da048b8/asyncpg/transaction.py#L57 -RedirectTemp /21-14 https://magicstack.github.io/asyncpg/current/usage.html#connection-pools -RedirectTemp /21-15 https://gist.github.com/jboner/2841832 -RedirectTemp /21-16 https://en.wikipedia.org/wiki/Network-attached_storage -RedirectTemp /21-17 https://en.wikipedia.org/wiki/Semaphore_(programming) -RedirectTemp /21-18 https://en.wikipedia.org/wiki/Semaphore_(programming) -RedirectTemp /21-19 https://groups.google.com/forum/#!msg/python-tulip/PdAEtwpaJHs/7fqb-Qj2zJoJ -RedirectTemp /21-20 https://web.archive.org/web/20151209151711/http://tritarget.org/blog/2012/11/28/the-pyramid-of-doom-a-javascript-style-trap -RedirectTemp /21-21 https://stackoverflow.com/questions/53701841/what-is-the-use-case-for-future-add-done-callback/53710563 -RedirectTemp /21-22 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor -RedirectTemp /21-23 https://motor.readthedocs.io/en/stable/ -RedirectTemp /21-24 https://emptysqua.re/blog/response-to-asynchronous-python-and-databases/ -RedirectTemp /21-25 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams -RedirectTemp /21-26 https://en.wikipedia.org/wiki/Phaistos_Disc -RedirectTemp /21-27 https://en.wikipedia.org/wiki/Inverted_index -RedirectTemp /21-28 https://fastapi.tiangolo.com/ -RedirectTemp /21-29 https://swagger.io/specification/ -RedirectTemp /21-30 https://asgi.readthedocs.io/en/latest/implementations.html -RedirectTemp /21-31 https://pydantic-docs.helpmanual.io/ -RedirectTemp /21-32 https://doc.traefik.io/traefik/ -RedirectTemp /21-33 https://fastapi.tiangolo.com/project-generation/ -RedirectTemp /21-34 https://fastapi.tiangolo.com/tutorial/response-model/ -RedirectTemp /21-35 https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server -RedirectTemp /21-36 https://github.com/python/typeshed/issues/5535 -RedirectTemp /21-37 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Server.serve_forever -RedirectTemp /21-38 https://docs.python.org/3/library/asyncio-stream.html#asyncio.StreamWriter.close -RedirectTemp /21-39 https://docs.python.org/3/library/asyncio-stream.html#streamwriter -RedirectTemp /21-40 https://docs.python.org/3/library/asyncio-stream.html -RedirectTemp /21-41 https://docs.python.org/3/library/asyncio-protocol.html -RedirectTemp /21-42 https://docs.python.org/3/library/asyncio-protocol.html#tcp-echo-server -RedirectTemp /21-43 https://github.com/aio-libs/aiopg -RedirectTemp /21-44 https://docs.python.org/3/whatsnew/3.8.html#asyncio -RedirectTemp /21-45 https://datatracker.ietf.org/doc/html/rfc6761 -RedirectTemp /21-46 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager -RedirectTemp /21-47 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager -RedirectTemp /21-48 https://docs.python.org/3/library/asyncio-task.html#asyncio.gather -RedirectTemp /21-49 https://curio.readthedocs.io/en/latest/index.html -RedirectTemp /21-50 https://curio.readthedocs.io/en/latest/reference.html#task-groups -RedirectTemp /21-51 https://en.wikipedia.org/wiki/Structured_concurrency -RedirectTemp /21-52 https://mail.python.org/archives/list/python-dev@python.org/thread/2ORDAW74LGE3ZI2QETPJRT2ZL7MCCPG2/ -RedirectTemp /21-53 https://www.python.org/dev/peps/pep-0654/#motivation -RedirectTemp /21-54 https://curio.readthedocs.io/en/latest/reference.html#AWAIT -RedirectTemp /21-55 https://www.python-httpx.org/async/#curio -RedirectTemp /21-56 https://github.com/dabeaz/curio/tree/78bca8a6ad677ef51e1568ac7b3e51441ab49c42/examples -RedirectTemp /21-57 https://datatracker.ietf.org/doc/html/rfc8305 -RedirectTemp /21-58 https://trio.readthedocs.io/en/stable/ -RedirectTemp /21-59 https://www.youtube.com/watch?v=M-sc73Y-zQA -RedirectTemp /21-60 https://en.wikipedia.org/wiki/Technical_debt -RedirectTemp /21-61 https://www.youtube.com/watch?v=E-1Y4kSsAFc -RedirectTemp /21-62 https://github.com/dabeaz/curio -RedirectTemp /21-63 https://trio.readthedocs.io/en/stable/ -RedirectTemp /21-64 https://curio.readthedocs.io/en/latest/#curio-university -RedirectTemp /21-65 https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/ -RedirectTemp /21-66 https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ -RedirectTemp /21-67 https://stackoverflow.com/questions/49482969/what-is-the-core-difference-between-asyncio-and-trio -RedirectTemp /21-68 https://docs.python.org/3/library/asyncio.html -RedirectTemp /21-69 https://bugs.python.org/issue33649 -RedirectTemp /21-70 https://docs.python.org/3/library/asyncio-dev.html -RedirectTemp /21-71 https://www.youtube.com/watch?v=iG6fr81xHKA -RedirectTemp /21-72 https://www.youtube.com/watch?v=F19R_M4Nay4 -RedirectTemp /21-73 https://asherman.io/projects/unsync.html -RedirectTemp /21-74 https://pyladies.com/ -RedirectTemp /21-75 https://www.youtube.com/watch?v=sW76-pRkZk8 -RedirectTemp /21-76 https://www.youtube.com/watch?v=Xbl7XjFYsN4 -RedirectTemp /21-77 https://www.youtube.com/watch?v=02CLD-42VdI -RedirectTemp /21-78 https://micropython.org/ -RedirectTemp /21-79 https://docs.micropython.org/en/latest/library/uasyncio.html -RedirectTemp /21-80 https://www.encode.io/articles/python-async-frameworks-beyond-developer-tribalism -RedirectTemp /21-81 https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ -RedirectTemp /21-82 https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ -RedirectTemp /21-83 https://github.com/MagicStack/uvloop -RedirectTemp /21-84 http://magic.io/blog/uvloop-blazing-fast-python-networking/ -RedirectTemp /21-85 https://github.com/MagicStack/httptools -RedirectTemp /21-86 https://docs.aiohttp.org/en/stable/ -RedirectTemp /21-87 https://github.com/wg/wrk -RedirectTemp /21-88 https://twistedmatrix.com/trac/ -############################################################ 22 -RedirectTemp /22-1 https://github.com/fluentpython/example-code-2e/blob/master/22-dyn-attr-prop/oscon/data/osconfeed.json -RedirectTemp /22-2 https://pypi.org/project/attrdict/ -RedirectTemp /22-3 https://pypi.org/project/addict/ -RedirectTemp /22-4 https://github.com/ActiveState/code/tree/master/recipes/Python/52308_simple_but_handy_collector_bunch_named_stuff -RedirectTemp /22-5 https://docs.python.org/3/library/types.html#types.SimpleNamespace -RedirectTemp /22-6 https://docs.python.org/3/library/argparse.html#argparse.Namespace -RedirectTemp /22-7 https://docs.python.org/3/library/multiprocessing.html#multiprocessing.managers.Namespace -RedirectTemp /22-8 https://github.com/fluentpython/example-code-2e/blob/master/22-dyn-attr-prop/oscon/schedule_v2.py -RedirectTemp /22-9 https://docs.python.org/3/library/functools.html#functools.cached_property -RedirectTemp /22-10 https://docs.python.org/3/library/functools.html#functools.cached_property -RedirectTemp /22-11 https://bugs.python.org/issue42781 -RedirectTemp /22-12 https://docs.python.org/3/howto/descriptor.html -RedirectTemp /22-13 https://github.com/python/cpython/blob/e6d0107e13ed957109e79b796984d3d026a8660d/Lib/functools.py#L926 -RedirectTemp /22-14 https://docs.python.org/3/library/threading.html#rlock-objects -RedirectTemp /22-15 https://docs.python.org/3.10/library/functools.html#functools.cached_property -RedirectTemp /22-16 https://www.wsj.com/articles/SB10001424052970203914304576627102996831200 -RedirectTemp /22-17 https://www.youtube.com/watch?v=s35rVw1zskA&feature=youtu.be -RedirectTemp /22-18 https://docs.python.org/3/library/functions.html#dir -RedirectTemp /22-19 https://github.com/python/cpython/blob/19903085c3ad7a17c8047e1556c700f2eb109931/Lib/cmd.py#L214 -RedirectTemp /22-20 https://docs.python.org/3/library/functions.html#hasattr -RedirectTemp /22-21 https://docs.python.org/3.10/reference/datamodel.html#special-method-lookup -RedirectTemp /22-22 https://docs.python.org/3/library/functions.html -RedirectTemp /22-23 https://docs.python.org/3/reference/datamodel.html#customizing-attribute-access -RedirectTemp /22-24 https://docs.python.org/3/reference/datamodel.html#special-method-lookup -RedirectTemp /22-25 https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /22-26 http://wiki.c2.com/?WelcomeVisitors -RedirectTemp /22-27 http://wiki.c2.com/?UniformAccessPrinciple -RedirectTemp /22-28 https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html -RedirectTemp /22-29 http://www.pingo.io/docs/ -RedirectTemp /22-30 https://www.drdobbs.com/javas-new-considered-harmful/184405016 -RedirectTemp /22-31 https://www.python.org/dev/peps/pep-0008/#class-names -############################################################ 23 -RedirectTemp /23-1 http://www.aleax.it/goo_pydp.pdf -RedirectTemp /23-2 https://docs.python.org/3.10/reference/datamodel.html#implementing-descriptors -RedirectTemp /23-3 https://docs.python.org/3/howto/descriptor.html -RedirectTemp /23-4 https://docs.python.org/3/howto/ -RedirectTemp /23-5 http://www.aleax.it/Python/nylug05_om.pdf -RedirectTemp /23-6 https://www.youtube.com/watch?v=VOzvpHoYQoo -RedirectTemp /23-7 https://www.python.org/dev/peps/pep-0487/#trait-descriptors -RedirectTemp /23-8 https://dreamsongs.com/RiseOfWorseIsBetter.html -RedirectTemp /23-9 http://web.archive.org/web/20031002184114/www.amk.ca/python/writing/warts.html -RedirectTemp /23-10 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this -RedirectTemp /23-11 http://python-history.blogspot.com/2009/02/adding-support-for-user-defined-classes.html -############################################################ 24 -RedirectTemp /24-1 https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /24-2 https://docs.djangoproject.com/en/3.2/topics/db/models/#meta-options -RedirectTemp /24-3 https://www.python.org/dev/peps/pep-3155/ -RedirectTemp /24-4 https://github.com/python/cpython/blob/3.9/Lib/collections/__init__.py -RedirectTemp /24-5 https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions -RedirectTemp /24-6 https://go.dev/tour/basics/12 -RedirectTemp /24-7 https://bugs.python.org/issue42102 -RedirectTemp /24-8 https://docs.python.org/3/reference/datamodel.html#object.__init_subclass__ -RedirectTemp /24-9 https://www.python.org/dev/peps/pep-0557/#abstract -RedirectTemp /24-10 https://github.com/python/cpython/blob/3.9/Lib/dataclasses.py -RedirectTemp /24-11 https://docs.python.org/3/reference/datamodel.html#creating-the-class-object -RedirectTemp /24-12 https://mail.python.org/pipermail/python-list/2002-December/134521.html -RedirectTemp /24-13 https://mail.python.org/pipermail/python-list/2002-July/162558.html -RedirectTemp /24-14 https://github.com/fluentpython/example-code/tree/master/21-class-metaprog/bulkfood -RedirectTemp /24-15 https://en.wikipedia.org/wiki/Principle_of_least_astonishment -RedirectTemp /24-16 https://docs.python.org/3/reference/datamodel.html#object.__class_getitem__ -RedirectTemp /24-17 https://en.wikipedia.org/wiki/Trait_(computer_programming) -RedirectTemp /24-18 https://en.wikipedia.org/wiki/Aspect-oriented_programming -RedirectTemp /24-19 https://dhh.dk/arc/000416.html -RedirectTemp /24-20 https://github.com/cjrh/autoslot -RedirectTemp /24-21 https://docs.python.org/3/reference/datamodel.html#customizing-class-creation -RedirectTemp /24-22 https://docs.python.org/3/library/functions.html#type -RedirectTemp /24-23 https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /24-24 https://docs.python.org/3/library/types.html -RedirectTemp /24-25 https://www.python.org/dev/peps/pep-3129/ -RedirectTemp /24-26 https://www.youtube.com/watch?v=cAGliEJV9_o -RedirectTemp /24-27 https://docs.python.org/3/library/functools.html#functools.total_ordering -RedirectTemp /24-28 https://www.python.org/download/releases/2.2.3/descrintro/ -RedirectTemp /24-29 https://github.com/lihaoyi/macropy -RedirectTemp /24-30 https://people.eecs.berkeley.edu/~bh/ss-toc2.html +# to update this file: +# cat custom.htaccess short.htaccess >> FPY.LI.htaccess diff --git a/links/README.md b/links/README.md index 6c9cfa8..2a3d80a 100644 --- a/links/README.md +++ b/links/README.md @@ -38,8 +38,15 @@ Exceptions: - URLs with `oreilly` in them are unchanged; - `fluentpython.com` URL (with no path) is unchanged; -The `custom.htacess` file contains the top redirects, which have custom names. -`FPY.LI.htaccess` has the same content, plus numbered URLs generated -from the links in each chapter in the book. +The `custom.htaccess` file contains redirects with custom names +plus numbered URLs generated from the links in each chapter in +the Second Edition in English. -The `FPY.LI.htaccess` is deployed at the root folder in `http://fpy.li`. +`short.htaccess` has redirects made by `short.py`, starting +with the Second Edition in Brazilian Portuguese. + +```shell +cat custom.htaccess short.htaccess > FPY.LI.htaccess +``` + +`FPY.LI.htaccess` is deployed at the root folder in `http://fpy.li`. diff --git a/links/custom.htaccess b/links/custom.htaccess index ad10802..cc779db 100644 --- a/links/custom.htaccess +++ b/links/custom.htaccess @@ -114,3 +114,959 @@ RedirectTemp /pep3141 https://www.python.org/dev/peps/pep-3141/ RedirectTemp /pep3148 https://www.python.org/dev/peps/pep-3148/ RedirectTemp /pep3155 https://www.python.org/dev/peps/pep-3155/ RedirectTemp /pep3333 https://www.python.org/dev/peps/pep-3333/ + +# Remaining URLs by chapter + +############################################################ p +RedirectTemp /p-1 https://mail.python.org/pipermail/python-list/2002-December/134521.html +RedirectTemp /p-2 https://docs.python.org/3.10/tutorial/ +RedirectTemp /p-3 https://docs.python.org/3/tutorial/ +RedirectTemp /p-4 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ +RedirectTemp /p-5 https://www.oreilly.com/online-learning/try-now.html +RedirectTemp /p-6 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ +RedirectTemp /p-7 https://stackoverflow.com/users/95810/alex-martelli +RedirectTemp /p-8 https://pythonpro.com.br +RedirectTemp /p-9 https://groups.google.com/g/python-brasil +RedirectTemp /p-10 https://www.coffeelab.com.br/ +RedirectTemp /p-11 https://garoa.net.br/wiki/P%C3%A1gina_principal +############################################################ a +RedirectTemp /a-1 https://groups.google.com/forum/#!topic/python-tulip/Y4bhLNbKs74 +RedirectTemp /a-2 https://docs.python.org/3/library/asyncio-eventloop.html#executor +RedirectTemp /a-3 https://www.youtube.com/watch?v=x-kB2o8sd5c +RedirectTemp /a-4 https://www.youtube.com/watch?v=OSGv2VnC0go +RedirectTemp /a-5 https://mail.python.org/pipermail/python-ideas/2015-March/032557.html +RedirectTemp /a-6 https://pypi.org/project/pep8/ +RedirectTemp /a-7 https://pypi.org/project/flake8/ +RedirectTemp /a-8 https://pypi.org/project/pyflakes/ +RedirectTemp /a-9 https://pypi.org/project/mccabe/ +RedirectTemp /a-10 https://google.github.io/styleguide/pyguide.html +RedirectTemp /a-11 https://flask.palletsprojects.com/en/1.1.x/styleguide/ +RedirectTemp /a-12 https://docs.python-guide.org/ +RedirectTemp /a-13 https://david.goodger.org/projects/pycon/2007/idiomatic/handout.html +RedirectTemp /a-14 https://docs.mongodb.com/manual/about/#about-the-documentation-process +RedirectTemp /a-15 https://blog.startifact.com/posts/older/what-is-pythonic.html +RedirectTemp /a-16 https://mail.python.org/pipermail/tutor/2003-October/thread.html#25930 +RedirectTemp /a-17 https://mail.python.org/pipermail/python-list/2003-April/192027.html +RedirectTemp /a-18 https://www.python.org/doc/essays/ +############################################################ 01 +RedirectTemp /1-1 http://hugunin.net/story_of_jython.html +RedirectTemp /1-2 https://www.oreilly.com/library/view/jython-essentials/9781449397364/ +RedirectTemp /1-3 https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers +RedirectTemp /1-4 https://docs.python.org/3.10/library/string.html#format-string-syntax +RedirectTemp /1-5 https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr +RedirectTemp /1-6 https://docs.python.org/3/library/stdtypes.html#truth +RedirectTemp /1-7 https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists +RedirectTemp /1-8 https://www.python.org/doc/humor/#the-zen-of-python +RedirectTemp /1-9 https://stackoverflow.com/users/95810/alex-martelli +RedirectTemp /1-10 https://en.wikipedia.org/wiki/Object_model +RedirectTemp /1-11 https://www.dourish.com/goodies/jargon.html +RedirectTemp /1-12 https://zopeinterface.readthedocs.io/en/latest/ +RedirectTemp /1-13 https://plone.org/ +############################################################ 02 +RedirectTemp /2-1 https://github.com/fluentpython/example-code-2e/blob/master/02-array-seq/listcomp_speed.py +RedirectTemp /2-2 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /2-3 https://stackoverflow.com/questions/68630/are-tuples-more-efficient-than-lists-in-python/22140115#22140115 +RedirectTemp /2-4 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations +RedirectTemp /2-5 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations +RedirectTemp /2-6 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching +RedirectTemp /2-7 https://docs.python.org/3.10/whatsnew/3.10.html +RedirectTemp /2-8 https://en.wikipedia.org/wiki/Switch_statement#Fallthrough +RedirectTemp /2-9 https://en.wikipedia.org/wiki/Dangling_else +RedirectTemp /2-10 https://github.com/gvanrossum/patma/blob/3ece6444ef70122876fd9f0099eb9490a2d630df/EXAMPLES.md#case-6-a-very-deep-iterable-and-type-match-with-extraction +RedirectTemp /2-11 https://github.com/fluentpython/lispy/blob/main/original/norvig/lis.py +RedirectTemp /2-12 https://norvig.com/lispy.html +RedirectTemp /2-13 https://numpy.org/doc/stable/user/quickstart.html#indexing-slicing-and-iterating +RedirectTemp /2-14 https://pythontutor.com/ +RedirectTemp /2-15 https://en.wikipedia.org/wiki/Fluent_interface +RedirectTemp /2-16 https://docs.python.org/3/library/bisect.html#bisect.insort +RedirectTemp /2-17 https://stackoverflow.com/questions/4845418/when-should-a-memoryview-be-used/ +RedirectTemp /2-18 https://www.fluentpython.com/extra/parsing-binary-struct/ +RedirectTemp /2-19 http://www.netlib.org +RedirectTemp /2-20 https://pandas.pydata.org/ +RedirectTemp /2-21 https://scikit-learn.org/stable/ +RedirectTemp /2-22 https://docs.python.org/3/howto/sorting.html +RedirectTemp /2-23 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /2-24 https://bugs.python.org/issue2292 +RedirectTemp /2-25 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching +RedirectTemp /2-26 https://docs.python.org/3.10/whatsnew/3.10.html +RedirectTemp /2-27 https://www.python.org/dev/peps/pep-0636/#appendix-a-quick-intro +RedirectTemp /2-28 https://eli.thegreenplace.net/2011/11/28/less-copies-in-python-with-the-buffer-protocol-and-memoryviews/ +RedirectTemp /2-29 https://jakevdp.github.io/PythonDataScienceHandbook/ +RedirectTemp /2-30 https://www.oreilly.com/library/view/python-for-data/9781491957653/ +RedirectTemp /2-31 https://www.labri.fr/perso/nrougier/from-python-to-numpy/ +RedirectTemp /2-32 https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html +RedirectTemp /2-33 http://www.fonts101.com/fonts/view/Uncategorized/34398/Dijkstra +RedirectTemp /2-34 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types +RedirectTemp /2-35 https://en.wikipedia.org/wiki/Timsort +RedirectTemp /2-36 http://www.groklaw.net/pdf3/OraGoogle-1202.pdf +RedirectTemp /2-37 https://www.python.org/doc/humor/#id9 +############################################################ 03 +RedirectTemp /3-1 https://www.python.org/dev/peps/pep-0584/#motivation +RedirectTemp /3-2 https://docs.python.org/3.10/c-api/typeobj.html#Py_TPFLAGS_MAPPING +RedirectTemp /3-3 https://docs.python.org/3/glossary.html#term-hashable +RedirectTemp /3-4 https://docs.python.org/3/glossary.html#term-hashable +RedirectTemp /3-5 http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf +RedirectTemp /3-6 https://github.com/pingo-io/pingo-py +RedirectTemp /3-7 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/missing.py +RedirectTemp /3-8 https://docs.python.org/3/library/collections.html#collections.ChainMap +RedirectTemp /3-9 https://docs.python.org/3/library/collections.html#collections.Counter +RedirectTemp /3-10 https://docs.python.org/3/library/shelve.html +RedirectTemp /3-11 https://docs.python.org/3/library/dbm.html +RedirectTemp /3-12 https://docs.python.org/3/library/pickle.html +RedirectTemp /3-13 https://nedbatchelder.com/blog/202006/pickles_nine_flaws.html +RedirectTemp /3-14 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py#L813 +RedirectTemp /3-15 https://mail.python.org/pipermail/python-dev/2015-May/140003.html +RedirectTemp /3-16 https://bugs.python.org/issue18986 +RedirectTemp /3-17 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/transformdict.py +RedirectTemp /3-18 http://gandenberger.org/2018/03/10/ordered-dicts-vs-ordereddict/ +RedirectTemp /3-19 https://www.pypy.org/ +RedirectTemp /3-20 https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html +RedirectTemp /3-21 https://www.npopov.com/2014/12/22/PHPs-new-hashtable-implementation.html +RedirectTemp /3-22 https://www.youtube.com/watch?v=66P5FMkWoVU +RedirectTemp /3-23 https://pyvideo.org/video/276/the-mighty-dictionary-55/ +RedirectTemp /3-24 https://www.youtube.com/watch?v=p33CVV29OG8 +RedirectTemp /3-25 https://docs.python.org/3/whatsnew/3.6.html#new-dict-implementation +RedirectTemp /3-26 https://github.com/python/cpython/blob/cf7eaa4617295747ee5646c4e2b7e7a16d7c64ab/Objects/dictobject.c +RedirectTemp /3-27 https://github.com/python/cpython/blob/cf7eaa4617295747ee5646c4e2b7e7a16d7c64ab/Objects/dictnotes.txt +RedirectTemp /3-28 https://www.youtube.com/watch?v=tGAngdU_8D8 +RedirectTemp /3-29 https://speakerdeck.com/ramalho/python-set-practice-at-pycon +RedirectTemp /3-30 https://github.com/standupdev/uintset +RedirectTemp /3-31 https://spectrum.ieee.org/hans-peter-luhn-and-the-birth-of-the-hashing-algorithm +RedirectTemp /3-32 http://www.json.org/fatfree.html +RedirectTemp /3-33 https://twitter.com/mitsuhiko/status/1229385843585974272 +############################################################ 04 +RedirectTemp /4-1 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 +RedirectTemp /4-2 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ +RedirectTemp /4-3 https://www.fluentpython.com/extra/parsing-binary-struct/ +RedirectTemp /4-4 https://www.fluentpython.com/extra/multi-character-emojis/ +RedirectTemp /4-5 https://w3techs.com/technologies/overview/character_encoding +RedirectTemp /4-6 https://docs.python.org/3/library/codecs.html#codecs.register_error +RedirectTemp /4-7 https://docs.python.org/3/library/stdtypes.html#str.isascii +RedirectTemp /4-8 https://pypi.org/project/chardet/ +RedirectTemp /4-9 https://docs.python.org/3/library/codecs.html#encodings-and-unicode +RedirectTemp /4-10 https://nedbatchelder.com/text/unipain/unipain.html +RedirectTemp /4-11 https://devblogs.microsoft.com/commandline/windows-command-line-unicode-and-utf-8-output-text-buffer/ +RedirectTemp /4-12 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONIOENCODING +RedirectTemp /4-13 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONLEGACYWINDOWSSTDIO +RedirectTemp /4-14 https://docs.python.org/3/library/locale.html#locale.getpreferredencoding +RedirectTemp /4-15 http://www.w3.org/TR/charmod-norm/ +RedirectTemp /4-16 https://docs.python.org/3/library/locale.html?highlight=strxfrm#locale.strxfrm +RedirectTemp /4-17 https://pypi.org/project/pyuca/ +RedirectTemp /4-18 https://github.com/jtauber/pyuca +RedirectTemp /4-19 http://www.unicode.org/Public/UCA/6.3.0/allkeys.txt +RedirectTemp /4-20 https://pypi.org/project/PyICU/ +RedirectTemp /4-21 https://docs.python.org/3.10/library/stdtypes.html#str.isalpha +RedirectTemp /4-22 https://en.wikipedia.org/wiki/Unicode_character_property#General_Category +RedirectTemp /4-23 https://en.wikipedia.org/wiki/Unicode_character_property +RedirectTemp /4-24 https://github.com/microsoft/terminal +RedirectTemp /4-25 https://docs.python.org/3/library/unicodedata.html +RedirectTemp /4-26 https://docs.python.org/3/reference/lexical_analysis.html#string-literal-concatenation +RedirectTemp /4-27 https://docs.python.org/3/library/re.html +RedirectTemp /4-28 https://nedbatchelder.com/text/unipain.html +RedirectTemp /4-29 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 +RedirectTemp /4-30 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ +RedirectTemp /4-31 https://regebro.wordpress.com/2011/03/23/unconfusing-unicode-what-is-unicode/ +RedirectTemp /4-32 https://docs.python.org/3/howto/unicode.html +RedirectTemp /4-33 https://diveintopython3.net/strings.html +RedirectTemp /4-34 https://diveintopython3.net/ +RedirectTemp /4-35 https://finderiko.com/python-book +RedirectTemp /4-36 https://docs.python.org/3.0/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit +RedirectTemp /4-37 https://lucumr.pocoo.org/2013/7/2/the-updated-guide-to-unicode/ +RedirectTemp /4-38 http://python-notes.curiousefficiency.org/en/latest/python3/binary_protocols.html +RedirectTemp /4-39 http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html +RedirectTemp /4-40 https://docs.python.org/3/library/codecs.html#standard-encodings +RedirectTemp /4-41 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Tools/unicode/listcodecs.py +RedirectTemp /4-42 https://www.oreilly.com/library/view/unicode-explained/059610121X/ +RedirectTemp /4-43 https://www.informit.com/store/unicode-demystified-a-practical-programmers-guide-to-9780201700527 +RedirectTemp /4-44 https://unicodebook.readthedocs.io/index.html +RedirectTemp /4-45 https://www.w3.org/International/wiki/Case_folding +RedirectTemp /4-46 http://www.w3.org/TR/charmod-norm/ +RedirectTemp /4-47 http://unicode.org/reports/tr15/ +RedirectTemp /4-48 http://www.unicode.org/faq/normalization.html +RedirectTemp /4-49 http://www.unicode.org/ +RedirectTemp /4-50 http://www.macchiato.com/unicode/nfc-faq +RedirectTemp /4-51 https://stories.moma.org/the-original-emoji-set-has-been-added-to-the-museum-of-modern-arts-collection-c6060e141f61?gi=c403f5a840a5 +RedirectTemp /4-52 https://emojipedia.org/ +RedirectTemp /4-53 https://blog.emojipedia.org/correcting-the-record-on-the-first-emoji-set/ +RedirectTemp /4-54 http://emojitracker.com/ +RedirectTemp /4-55 http://www.unicode.org/glossary/#plain_text +RedirectTemp /4-56 http://www.methods.co.nz/asciidoc/ +RedirectTemp /4-57 https://atlas.oreilly.com/ +############################################################ 05 +RedirectTemp /5-1 https://docs.python.org/3/library/typing.html#typing.TypedDict +RedirectTemp /5-2 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations +RedirectTemp /5-3 https://docs.python.org/3/library/typing.html#typing.get_type_hints +RedirectTemp /5-4 https://docs.python.org/3.8/library/collections.html#collections.somenamedtuple._asdict +RedirectTemp /5-5 https://www.jetbrains.com/pycharm/ +RedirectTemp /5-6 https://www.python.org/dev/peps/pep-0484/#acceptable-type-hints +RedirectTemp /5-7 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass +RedirectTemp /5-8 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass +RedirectTemp /5-9 https://docs.python.org/3/library/dataclasses.html +RedirectTemp /5-10 https://docs.python.org/3/library/dataclasses.html#inheritance +RedirectTemp /5-11 https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations +RedirectTemp /5-12 https://dublincore.org/specifications/dublin-core/ +RedirectTemp /5-13 https://en.wikipedia.org/wiki/Dublin_Core +RedirectTemp /5-14 https://martinfowler.com/bliki/CodeSmell.html +RedirectTemp /5-15 https://martinfowler.com/books/refactoring.html +RedirectTemp /5-16 https://www.python.org/dev/peps/pep-0634/#class-patterns +RedirectTemp /5-17 https://docs.python.org/3/library/dataclasses.html +RedirectTemp /5-18 https://www.python.org/dev/peps/pep-0557/#id47 +RedirectTemp /5-19 https://www.python.org/dev/peps/pep-0557/#id48 +RedirectTemp /5-20 https://www.python.org/dev/peps/pep-0557/#id33 +RedirectTemp /5-21 https://realpython.com +RedirectTemp /5-22 https://realpython.com/python-data-classes/ +RedirectTemp /5-23 https://www.youtube.com/watch?v=T-TwcmT6Rcw +RedirectTemp /5-24 https://www.attrs.org/en/stable/ +RedirectTemp /5-25 https://glyph.twistedmatrix.com/2016/08/attrs.html +RedirectTemp /5-26 https://www.attrs.org/en/stable/why.html +RedirectTemp /5-27 https://github.com/dabeaz/cluegen +RedirectTemp /5-28 https://refactoring.guru/ +RedirectTemp /5-29 https://refactoring.guru/smells/data-class +RedirectTemp /5-30 https://web.archive.org/web/20190204130328/http://catb.org/esr/jargon/html/G/Guido.html +RedirectTemp /5-31 https://web.archive.org/web/20190211161610/http://catb.org/esr/jargon/html/index.html +RedirectTemp /5-32 https://www.attrs.org/en/stable/ +############################################################ 06 +RedirectTemp /6-1 https://www.olin.edu/faculty/profile/lynn-andrea-stein/ +RedirectTemp /6-2 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types +RedirectTemp /6-3 https://pythontutor.com/ +RedirectTemp /6-4 https://docs.python.org/3/library/copy.html +RedirectTemp /6-5 https://en.wikipedia.org/wiki/Principle_of_least_astonishment +RedirectTemp /6-6 https://docs.python.org/3/reference/datamodel.html#object.%5C_%5C_del__ +RedirectTemp /6-7 https://emptysqua.re/blog/pypy-garbage-collection-and-a-deadlock/ +RedirectTemp /6-8 https://www.youtube.com/watch?v=HHFCFJSPWrI&feature=youtu.be +RedirectTemp /6-9 http://pymotw.com/3/copy/ +RedirectTemp /6-10 http://pymotw.com/3/weakref/ +RedirectTemp /6-11 https://docs.python.org/3/library/gc.html +RedirectTemp /6-12 https://devguide.python.org/garbage_collector/ +RedirectTemp /6-13 https://devguide.python.org/ +RedirectTemp /6-14 https://www.python.org/dev/peps/pep-0442/ +RedirectTemp /6-15 https://en.wikipedia.org/wiki/String_interning +RedirectTemp /6-16 https://en.wikipedia.org/wiki/Haddocks%27_Eyes +RedirectTemp /6-17 https://thp.io/2012/python-gc/python_gc_final_2012-01-22.pdf +############################################################ 07 +RedirectTemp /7-1 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html +RedirectTemp /7-2 https://www.fluentpython.com/extra/function-introspection/ +RedirectTemp /7-3 https://docs.python.org/3/library/functions.html#map +RedirectTemp /7-4 https://en.wikipedia.org/wiki/Functional_programming +RedirectTemp /7-5 https://docs.python.org/3/howto/functional.html +RedirectTemp /7-6 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy +RedirectTemp /7-7 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters +RedirectTemp /7-8 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters +RedirectTemp /7-9 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/functools.py#L341 +RedirectTemp /7-10 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy +RedirectTemp /7-11 https://docs.python.org/3/howto/functional.html +RedirectTemp /7-12 https://stackoverflow.com/questions/3252228/python-why-is-functools-partial-necessary +RedirectTemp /7-13 https://speakerdeck.com/ramalho/beyond-paradigms-berlin-edition +RedirectTemp /7-14 https://www.youtube.com/watch?v=bF3a2VYXxa0 +RedirectTemp /7-15 http://cs.brown.edu/~sk/Publications/Papers/Published/sk-teach-pl-post-linnaean/ +RedirectTemp /7-16 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html +RedirectTemp /7-17 https://raw.githubusercontent.com/python/cpython/main/Misc/HISTORY +RedirectTemp /7-18 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html +############################################################ 08 +RedirectTemp /8-1 https://www.python.org/dev/peps/pep-0484/#non-goals +RedirectTemp /8-2 https://github.com/python/typing/issues/182 +RedirectTemp /8-3 https://github.com/python/mypy/issues/731 +RedirectTemp /8-4 https://github.com/google/pytype +RedirectTemp /8-5 https://github.com/Microsoft/pyright +RedirectTemp /8-6 https://pyre-check.org/ +RedirectTemp /8-7 https://mypy.readthedocs.io/en/stable/introduction.html +RedirectTemp /8-8 https://mypy.readthedocs.io/en/stable/config_file.html +RedirectTemp /8-9 https://pypi.org/project/flake8/ +RedirectTemp /8-10 https://pypi.org/project/blue/ +RedirectTemp /8-11 https://pypi.org/project/black/ +RedirectTemp /8-12 https://wefearchange.org/2020/11/steeringcouncil.rst.html +RedirectTemp /8-13 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes +RedirectTemp /8-14 https://en.wikipedia.org/wiki/Barbara_Liskov +RedirectTemp /8-15 https://en.wikipedia.org/wiki/Behavioral_subtyping +RedirectTemp /8-16 https://www.python.org/dev/peps/pep-0585/#implementation +RedirectTemp /8-17 https://docs.python.org/3/library/typing.html#module-contents +RedirectTemp /8-18 https://en.wikipedia.org/wiki/Geohash +RedirectTemp /8-19 https://en.wikipedia.org/wiki/Inverted_index +RedirectTemp /8-20 https://docs.python.org/3/library/typing.html#typing.List +RedirectTemp /8-21 https://docs.python.org/3/library/typing.html#typing.Dict +RedirectTemp /8-22 https://docs.python.org/3/library/typing.html#typing.Set +RedirectTemp /8-23 https://www.python.org/dev/peps/pep-0585/#implementation +RedirectTemp /8-24 https://docs.python.org/3/library/numbers.html +RedirectTemp /8-25 https://docs.python.org/3/library/typing.html#typing.List +RedirectTemp /8-26 https://github.com/python/typeshed +RedirectTemp /8-27 https://github.com/python/typeshed/blob/66cd36268a6a667714efaa27198a41d0d7f89477/stdlib/2and3/math.pyi#L45 +RedirectTemp /8-28 https://docs.python.org/3/library/statistics.html#statistics.mode +RedirectTemp /8-29 https://github.com/python/cpython/blob/822efa5695b5ba6c2316c1400e4e9ec2546f7ea5/Lib/statistics.py#L534 +RedirectTemp /8-30 https://github.com/python/typeshed/blob/e1e99245bb46223928eba68d4fc74962240ba5b4/stdlib/3/statistics.pyi +RedirectTemp /8-31 https://docs.python.org/3/library/statistics.html#statistics.mode +RedirectTemp /8-32 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/3/statistics.pyi#L32 +RedirectTemp /8-33 https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ +RedirectTemp /8-34 https://docs.python.org/3/library/typing.html#typing.Callable +RedirectTemp /8-35 https://pypi.org/project/blue/ +RedirectTemp /8-36 https://www.python.org/dev/peps/pep-0484/#id38 +RedirectTemp /8-37 https://docs.google.com/document/d/1aXs1tpwzPjW9MdsG5dI7clNFyYayFBkcXwRDo-qvbIk/preview +RedirectTemp /8-38 https://www.oreilly.com/library/view/the-best-software/9781590595008/ +RedirectTemp /8-39 https://www.youtube.com/watch?v=YFexUDjHO6w +RedirectTemp /8-40 https://www.youtube.com/watch?v=YFexUDjHO6w&t=13m40s +RedirectTemp /8-41 https://bernat.tech/posts/the-state-of-type-hints-in-python/ +RedirectTemp /8-42 https://realpython.com/python-type-checking/ +RedirectTemp /8-43 https://cjolowicz.github.io/posts/hypermodern-python-04-typing/ +RedirectTemp /8-44 https://mypy.readthedocs.io/en/stable/index.html +RedirectTemp /8-45 https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html +RedirectTemp /8-46 https://mypy.readthedocs.io/en/stable/common_issues.html +RedirectTemp /8-47 https://github.com/typeddjango/awesome-python-typing +RedirectTemp /8-48 https://docs.python.org/3/library/functions.html#max +RedirectTemp /8-49 https://en.wikipedia.org/wiki/Linguistic_relativity +RedirectTemp /8-50 https://pypistats.org/top +RedirectTemp /8-51 https://github.com/psf/requests/issues/3855 +RedirectTemp /8-52 https://lwn.net/Articles/643399/ +RedirectTemp /8-53 https://docs.python-requests.org/en/master/api/#requests.request +RedirectTemp /8-54 https://queue.acm.org/detail.cfm?id=1039523 +############################################################ 09 +RedirectTemp /9-1 https://docs.python.org/3/library/dis.html +RedirectTemp /9-2 https://en.wikipedia.org/wiki/Memoization +RedirectTemp /9-3 https://numpy.org/doc/stable/user/basics.types.html +RedirectTemp /9-4 https://docs.python.org/3/library/functools.html#functools.singledispatch +RedirectTemp /9-5 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/README.md +RedirectTemp /9-6 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/01-how-you-implemented-your-python-decorator-is-wrong.md +RedirectTemp /9-7 https://wrapt.readthedocs.io/en/latest/ +RedirectTemp /9-8 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch09.html +RedirectTemp /9-9 https://pypi.org/project/decorator/ +RedirectTemp /9-10 https://wiki.python.org/moin/PythonDecoratorLibrary +RedirectTemp /9-11 http://web.archive.org/web/20201109032203/http://effbot.org/zone/closure.htm +RedirectTemp /9-12 https://www.python.org/dev/peps/pep-3104/ +RedirectTemp /9-13 https://www.python.org/dev/peps/pep-0227/ +RedirectTemp /9-14 https://www.python.org/dev/peps/pep-0443/ +RedirectTemp /9-15 https://www.artima.com/weblogs/viewpost.jsp?thread=101605 +RedirectTemp /9-16 https://reg.readthedocs.io/en/latest/ +RedirectTemp /9-17 https://morepath.readthedocs.io/en/latest/ +RedirectTemp /9-18 https://www.gnu.org/software/emacs/manual/html_node/elisp/Dynamic-Binding.html +RedirectTemp /9-19 http://www.paulgraham.com/rootsoflisp.html +RedirectTemp /9-20 http://www-formal.stanford.edu/jmc/recursive/recursive.html +RedirectTemp /9-21 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this +############################################################ 10 +RedirectTemp /10-1 https://en.wikipedia.org/wiki/Software_design_pattern +RedirectTemp /10-2 https://en.wikipedia.org/wiki/Iterator_pattern +RedirectTemp /10-3 https://github.com/python/mypy/issues/9397 +RedirectTemp /10-4 http://www.norvig.com/design-patterns/index.htm +RedirectTemp /10-5 https://pyvideo.org/video/1110/python-design-patterns/ +RedirectTemp /10-6 http://www.aleax.it/gdd_pydp.pdf +RedirectTemp /10-7 https://perl.plover.com/yak/design/ +RedirectTemp /10-8 https://en.wikipedia.org/wiki/Turtles_all_the_way_down +############################################################ 11 +RedirectTemp /11-1 https://blog.startifact.com/posts/older/what-is-pythonic.html +RedirectTemp /11-2 https://julien.danjou.info/guide-python-static-class-abstract-methods/ +RedirectTemp /11-3 https://docs.python.org/3/library/string.html#formatspec +RedirectTemp /11-4 https://docs.python.org/3/reference/lexical_analysis.html#f-strings +RedirectTemp /11-5 https://docs.python.org/3/library/string.html#format-string-syntax +RedirectTemp /11-6 https://docs.python.org/3/library/string.html#formatspec +RedirectTemp /11-7 https://docs.python.org/3/reference/datamodel.html#object.__hash__ +RedirectTemp /11-8 https://web.archive.org/web/20161025185040/http://pythonpaste.org/StyleGuide.html +RedirectTemp /11-9 https://docs.python.org/3/tutorial/modules.html#more-on-modules +RedirectTemp /11-10 https://docs.python.org/3/library/gettext.html#gettext.NullTranslations +RedirectTemp /11-11 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/mem_test.py +RedirectTemp /11-12 https://docs.python.org/3/reference/datamodel.html#basic-customization +RedirectTemp /11-13 http://esug.org/data/HistoricalDocuments/TheSmalltalkReport/ST07/04wo.pdf +RedirectTemp /11-14 https://www.artima.com/articles/the-simplest-thing-that-could-possibly-work#part3 +RedirectTemp /11-15 https://docs.oracle.com/javase/tutorial/essential/environment/security.html +RedirectTemp /11-16 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/private/Expose.java +RedirectTemp /11-17 https://docs.oracle.com/javase/tutorial/essential/environment/security.html +############################################################ 12 +RedirectTemp /12-1 https://en.wikipedia.org/wiki/Vector_space_model +RedirectTemp /12-2 https://pypi.org/project/gensim/ +RedirectTemp /12-3 https://docs.python.org/3/library/functions.html#enumerate +RedirectTemp /12-4 https://mathworld.wolfram.com/Hypersphere.html +RedirectTemp /12-5 https://en.wikipedia.org/wiki/Fold_(higher-order_function) +RedirectTemp /12-6 https://docs.python.org/2.5/whatsnew/pep-357.html +RedirectTemp /12-7 https://docs.python.org/3/reference/datamodel.html#special-method-names +RedirectTemp /12-8 https://en.wikipedia.org/wiki/KISS_principle +RedirectTemp /12-9 https://mail.python.org/pipermail/python-list/2000-July/046184.html +RedirectTemp /12-10 https://en.wikipedia.org/wiki/Duck_typing +RedirectTemp /12-11 https://mail.python.org/mailman/listinfo/python-list +RedirectTemp /12-12 https://mail.python.org/pipermail/python-list/2003-April/218568.html +############################################################ 13 +RedirectTemp /13-1 https://docs.python.org/3/c-api/index.html +RedirectTemp /13-2 https://docs.python.org/3/c-api/sequence.html +RedirectTemp /13-3 https://github.com/python/cpython/blob/31ceccb2c77854893f3a754aca04bedd74bedb10/Lib/_collections_abc.py#L870 +RedirectTemp /13-4 https://en.wikipedia.org/wiki/Monkey_patch +RedirectTemp /13-5 https://www.gevent.org/api/gevent.monkey.html +RedirectTemp /13-6 https://docs.python.org/3/library/random.html#random.shuffle +RedirectTemp /13-7 https://docs.python.org/3/reference/datamodel.html#emulating-container-types +RedirectTemp /13-8 https://docs.python.org/3/library/collections.html#collections.namedtuple +RedirectTemp /13-9 https://github.com/python/typeshed/blob/24afb531ffd07083d6a74be917342195062f7277/stdlib/collections/__init__.pyi +RedirectTemp /13-10 https://docs.python.org/3/glossary.html#term-abstract-base-class +RedirectTemp /13-11 https://en.wikipedia.org/wiki/Duck_typing#History +RedirectTemp /13-12 http://ptgmedia.pearsoncmg.com/images/020163371x/items/item33.html +RedirectTemp /13-13 https://docs.python.org/3/library/bisect.html#bisect.bisect +RedirectTemp /13-14 https://github.com/python/cpython/blob/main/Lib/_collections_abc.py +RedirectTemp /13-15 https://github.com/python/cpython/blob/main/Lib/abc.py +RedirectTemp /13-16 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes +RedirectTemp /13-17 https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable +RedirectTemp /13-18 https://docs.python.org/3/library/abc.html +RedirectTemp /13-19 https://docs.python.org/dev/library/abc.html#abc.abstractmethod +RedirectTemp /13-20 https://docs.python.org/dev/library/abc.html +RedirectTemp /13-21 https://docs.python.org/3/library/os.html#os.urandom +RedirectTemp /13-22 https://github.com/python/mypy/issues/2922 +RedirectTemp /13-23 https://docs.python.org/3/library/stdtypes.html#truth +RedirectTemp /13-24 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py +RedirectTemp /13-25 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L369 +RedirectTemp /13-26 https://github.com/python/cpython/blob/c0a9afe2ac1820409e6173bd1893ebee2cf50270/Lib/abc.py#L196 +RedirectTemp /13-27 https://bugs.python.org/issue31333 +RedirectTemp /13-28 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Modules/_abc.c#L605 +RedirectTemp /13-29 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L881 +RedirectTemp /13-30 https://docs.python.org/3/library/typing.html#protocols +RedirectTemp /13-31 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Lib/typing.py#L1751 +RedirectTemp /13-32 https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ +RedirectTemp /13-33 https://martinfowler.com/bliki/RoleInterface.html +RedirectTemp /13-34 https://en.wikipedia.org/wiki/Interface_segregation_principle +RedirectTemp /13-35 https://github.com/python/typeshed/blob/master/CONTRIBUTING.md +RedirectTemp /13-36 https://gist.github.com/asukakenji/ac8a05644a2e98f1d5ea8c299541fce9 +RedirectTemp /13-37 https://www.python.org/dev/peps/pep-0544/#runtime-checkable-decorator-and-narrowing-types-by-isinstance +RedirectTemp /13-38 https://www.python.org/dev/peps/pep-0544/#merging-and-extending-protocols +RedirectTemp /13-39 https://numpy.org/devdocs/user/basics.types.html +RedirectTemp /13-40 https://github.com/python/typeshed/blob/master/stdlib/statistics.pyi +RedirectTemp /13-41 https://bugs.python.org/issue41974 +RedirectTemp /13-42 https://glyph.twistedmatrix.com/2020/07/new-duck.html +RedirectTemp /13-43 https://glyph.twistedmatrix.com/2021/03/interfaces-and-protocols.html +RedirectTemp /13-44 https://plone.org/ +RedirectTemp /13-45 https://trypyramid.com/ +RedirectTemp /13-46 https://twistedmatrix.com/trac/ +RedirectTemp /13-47 https://www.artima.com/articles/contracts-in-python +RedirectTemp /13-48 https://martinfowler.com/bliki/DynamicTyping.html +RedirectTemp /13-49 https://martinfowler.com/bliki/RoleInterface.html +RedirectTemp /13-50 https://mypy.readthedocs.io/en/stable/protocols.html +RedirectTemp /13-51 https://pymotw.com/3/abc/index.html +RedirectTemp /13-52 https://www.python.org/dev/peps/pep-3119/ +RedirectTemp /13-53 https://www.python.org/dev/peps/pep-3141/ +RedirectTemp /13-54 https://docs.python.org/3/library/numbers.html +RedirectTemp /13-55 https://github.com/python/mypy/issues/3186 +RedirectTemp /13-56 https://stackoverflow.com/questions/69334475/how-to-hint-at-number-types-i-e-subclasses-of-number-not-numbers-themselv/69383462#69383462 +RedirectTemp /13-57 https://github.com/python/mypy/issues/3186 +RedirectTemp /13-58 https://martinfowler.com/articles/lean-inception/ +RedirectTemp /13-59 https://martinfowler.com +RedirectTemp /13-60 https://www.jetbrains.com/pycharm/ +RedirectTemp /13-61 https://wingware.com/ +RedirectTemp /13-62 https://code.visualstudio.com/ +############################################################ 14 +RedirectTemp /14-1 http://worrydream.com/EarlyHistoryOfSmalltalk/ +RedirectTemp /14-2 https://docs.python.org/3/tutorial/classes.html +RedirectTemp /14-3 https://docs.python.org/3/library/collections.html#ordereddict-examples-and-recipes +RedirectTemp /14-4 https://discuss.python.org/t/is-it-time-to-deprecate-unbound-super-methods/1833 +RedirectTemp /14-5 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /14-6 https://docs.python.org/3/library/collections.html +RedirectTemp /14-7 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/strkeydict_dictsub.py +RedirectTemp /14-8 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /14-9 https://en.wikipedia.org/wiki/Breadth-first_search +RedirectTemp /14-10 https://www.python.org/download/releases/2.3/mro/ +RedirectTemp /14-11 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/uppermixin.py +RedirectTemp /14-12 https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html +RedirectTemp /14-13 https://docs.python.org/3/library/collections.abc.html +RedirectTemp /14-14 https://github.com/python/cpython/blob/8ece98a7e418c3c68a4c61bc47a2d0931b59a889/Lib/collections/__init__.py#L1084 +RedirectTemp /14-15 https://docs.python.org/3/library/http.server.html +RedirectTemp /14-16 https://github.com/python/cpython/blob/17c23167942498296f0bdfffe52e72d53d66d693/Lib/http/server.py#L144 +RedirectTemp /14-17 https://github.com/python/cpython/blob/699ee016af5736ffc80f68359617611a22b72943/Lib/socketserver.py#L664 +RedirectTemp /14-18 https://docs.python.org/3/library/socketserver.html#socketserver.ForkingMixIn +RedirectTemp /14-19 https://docs.python.org/3/library/os.html#os.fork +RedirectTemp /14-20 https://en.wikipedia.org/wiki/POSIX +RedirectTemp /14-21 http://ccbv.co.uk/ +RedirectTemp /14-22 https://github.com/django/django/tree/main/django/views/generic +RedirectTemp /14-23 https://en.wikipedia.org/wiki/Template_method_pattern +RedirectTemp /14-24 https://docs.python.org/3/library/tkinter.html +RedirectTemp /14-25 https://docs.python.org/3/library/tkinter.ttk.html +RedirectTemp /14-26 https://docs.oracle.com/javase/10/docs/api/java/awt/package-tree.html +RedirectTemp /14-27 https://docs.oracle.com/javase/10/docs/api/javax/swing/package-tree.html +RedirectTemp /14-28 https://squeak.org/ +RedirectTemp /14-29 https://github.com/django/django/blob/b64db05b9cedd96905d637a2d824cbbf428e40e7/django/views/generic/list.py#L194 +RedirectTemp /14-30 https://github.com/python/cpython/blob/8ed183391241f0c73e7ba7f42b1d49fc02985f7b/Lib/tkinter/__init__.py#L2618 +RedirectTemp /14-31 https://docs.python.org/3/library/socketserver.html +RedirectTemp /14-32 https://docs.python.org/3/library/socketserver.html#socketserver.BaseServer +RedirectTemp /14-33 https://github.com/python/cpython/blob/699ee016af5736ffc80f68359617611a22b72943/Lib/socketserver.py#L153 +RedirectTemp /14-34 https://docs.python.org/3/library/typing.html#typing.final +RedirectTemp /14-35 https://docs.python.org/3/library/typing.html#typing.Final +RedirectTemp /14-36 https://docs.python.org/3/library/collections.abc.html +RedirectTemp /14-37 https://hynek.me/articles/python-subclassing-redux/ +RedirectTemp /14-38 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch08.html#super +RedirectTemp /14-39 https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ +RedirectTemp /14-40 https://fuhm.net/super-harmful/ +RedirectTemp /14-41 https://stackoverflow.com/questions/30190185/how-to-use-super-with-one-argument/30190341#30190341 +RedirectTemp /14-42 https://www.artima.com/weblogs/viewpost.jsp?thread=246488 +RedirectTemp /14-43 https://www.artima.com/weblogs/viewpost.jsp?thread=281127 +RedirectTemp /14-44 https://www.artima.com/weblogs/viewpost.jsp?thread=246341 +RedirectTemp /14-45 https://www.artima.com/weblogs/viewpost.jsp?thread=246483 +RedirectTemp /14-46 https://www.artima.com/weblogs/viewpost.jsp?thread=236275 +RedirectTemp /14-47 https://www.artima.com/weblogs/viewpost.jsp?thread=236278 +RedirectTemp /14-48 https://www.artima.com/weblogs/viewpost.jsp?thread=237121 +RedirectTemp /14-49 https://python-patterns.guide/gang-of-four/composition-over-inheritance/ +RedirectTemp /14-50 https://python-patterns.guide/ +RedirectTemp /14-51 https://www.youtube.com/watch?v=3MNVP9-hglc +RedirectTemp /14-52 http://worrydream.com/EarlyHistoryOfSmalltalk/ +RedirectTemp /14-53 https://en.wikipedia.org/wiki/Polymorphism_(computer_science) +############################################################ 15 +RedirectTemp /15-1 https://www.youtube.com/watch?v=csL8DLXGNlU&t=92m5s +RedirectTemp /15-2 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/2and3/builtins.pyi#L1434 +RedirectTemp /15-3 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/2and3/builtins.pyi +RedirectTemp /15-4 https://twitter.com/gwidion/status/1265384692464967680 +RedirectTemp /15-5 https://pypi.org/project/pydantic/ +RedirectTemp /15-6 https://google.github.io/pytype/faq.html +RedirectTemp /15-7 https://google.github.io/pytype/faq.html +RedirectTemp /15-8 https://lxml.de/ +RedirectTemp /15-9 https://docs.python.org/3/library/xml.etree.elementtree.html +RedirectTemp /15-10 https://mypy.readthedocs.io/en/stable/common_issues.html +RedirectTemp /15-11 https://mypy.readthedocs.io/en/stable/common_issues.html#types-of-empty-collections +RedirectTemp /15-12 https://github.com/python/typing/issues/182 +RedirectTemp /15-13 https://pypi.org/project/pydantic/ +RedirectTemp /15-14 https://mypy.readthedocs.io/en/stable/type_narrowing.html#casts +RedirectTemp /15-15 https://github.com/python/cpython/blob/bee66d3cb98e740f9d8057eb7f503122052ca5d8/Lib/typing.py#L1340 +RedirectTemp /15-16 https://www.python.org/dev/peps/pep-0484/#casts +RedirectTemp /15-17 https://github.com/python/typeshed/issues/5535 +RedirectTemp /15-18 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams +RedirectTemp /15-19 https://github.com/python/cpython/blob/b798ab06937f8bb24b444a49dd42e11fff15e654/Lib/test/test_asyncio/test_server.py#L55 +RedirectTemp /15-20 https://en.wikipedia.org/wiki/Code_smell +RedirectTemp /15-21 https://mail.python.org/archives/list/typing-sig@python.org/message/5LCWMN2UY2UQNLC5Z47GHBZKSPZW4I63/ +RedirectTemp /15-22 https://mypy.readthedocs.io/en/stable/error_codes.html#error-codes +RedirectTemp /15-23 https://github.com/fluentpython/example-code-2e/blob/master/15-more-types/clip_annot.py +RedirectTemp /15-24 https://docs.python.org/3/library/typing.html#introspection-helpers +RedirectTemp /15-25 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations +RedirectTemp /15-26 https://www.python.org/dev/peps/pep-0563/#abstract +RedirectTemp /15-27 https://mail.python.org/archives/list/python-dev@python.org/message/ZBJ7MD6CSGM6LZAOTET7GXAVBZB7O77O/ +RedirectTemp /15-28 https://docs.python.org/3.10/howto/annotations.html +RedirectTemp /15-29 https://docs.python.org/3/library/typing.html#user-defined-generic-types +RedirectTemp /15-30 https://github.com/python/typeshed/blob/bfc83c365a0b26ab16586beac77ff16729d0e473/stdlib/builtins.pyi#L743 +RedirectTemp /15-31 https://docs.python.org/3.10/library/typing.html#typing.FrozenSet +RedirectTemp /15-32 https://docs.python.org/3.10/library/typing.html#typing.Generator +RedirectTemp /15-33 https://docs.python.org/3.10/library/typing.html#typing.AsyncGenerator +RedirectTemp /15-34 https://github.com/python/cpython/blob/46b16d0bdbb1722daed10389e27226a2370f1635/Lib/typing.py#L1786 +RedirectTemp /15-35 https://github.com/python/typeshed/blob/2a9f081abbf01134e4e04ced6a750107db904d70/stdlib/builtins.pyi#L239 +RedirectTemp /15-36 https://www.oreilly.com/library/view/robust-python/9781098100650/ +RedirectTemp /15-37 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance +RedirectTemp /15-38 https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generic-types +RedirectTemp /15-39 https://mypy.readthedocs.io/en/stable/common_issues.html#variance +RedirectTemp /15-40 https://www.artima.com/weblogs/viewpost.jsp?thread=85551 +RedirectTemp /15-41 https://dl.acm.org/action/cookieAbsent +RedirectTemp /15-42 http://bracha.org/pluggableTypesPosition.pdf +RedirectTemp /15-43 https://www.researchgate.net/publication/213886116_Static_Typing_Where_Possible_Dynamic_Typing_When_Needed_The_End_of_the_Cold_War_Between_Programming_Languages +RedirectTemp /15-44 https://www.atomickotlin.com/atomickotlin/ +RedirectTemp /15-45 https://www.informit.com/store/effective-java-9780134685991 +RedirectTemp /15-46 https://www.manning.com/books/programming-with-types +RedirectTemp /15-47 https://www.oreilly.com/library/view/programming-typescript/9781492037644/ +RedirectTemp /15-48 https://www.informit.com/store/dart-programming-language-9780321927705 +RedirectTemp /15-49 https://www.yodaiken.com/2017/09/15/bad-ideas-in-type-theory/ +RedirectTemp /15-50 https://www.yodaiken.com/2017/11/30/types-considered-harmful-ii/ +RedirectTemp /15-51 https://web.archive.org/web/20071010002142/http://weblogs.java.net/blog/arnold/archive/2005/06/generics_consid_1.html +RedirectTemp /15-52 https://github.com/python/cpython/blob/3e7ee02327db13e4337374597cdc4458ecb9e3ad/Lib/asyncio/trsock.py#L5 +RedirectTemp /15-53 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance +############################################################ 16 +RedirectTemp /16-1 http://www.gotw.ca/publications/c_family_interview.htm +RedirectTemp /16-2 https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations +RedirectTemp /16-3 https://docs.python.org/3/reference/datamodel.html#object.__neg__ +RedirectTemp /16-4 https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#boolean-indexing +RedirectTemp /16-5 https://docs.python.org/3/library/collections.html#collections.Counter +RedirectTemp /16-6 https://docs.python.org/3/reference/datamodel.html#emulating-container-types +RedirectTemp /16-7 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations +RedirectTemp /16-8 https://www.fluentpython.com/lingo/#fail-fast +RedirectTemp /16-9 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Objects/typeobject.c#L4598 +RedirectTemp /16-10 https://neopythonic.blogspot.com/2019/03/why-operators-are-useful.html +RedirectTemp /16-11 https://treyhunner.com/2019/03/python-deep-comparisons-and-code-readability/ +RedirectTemp /16-12 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations +RedirectTemp /16-13 https://docs.python.org/3/library/pathlib.html +RedirectTemp /16-14 https://pypi.org/project/scapy/ +RedirectTemp /16-15 https://scapy.readthedocs.io/en/latest/usage.html#stacking-layers +RedirectTemp /16-16 https://docs.python.org/3/library/functools.html#functools.total_ordering +RedirectTemp /16-17 https://wiki.illinois.edu//wiki/download/attachments/273416327/ingalls.pdf +RedirectTemp /16-18 https://wiki.illinois.edu//wiki/download/attachments/273416327/double-dispatch.pdf +RedirectTemp /16-19 http://www.gotw.ca/publications/c_family_interview.htm +RedirectTemp /16-20 http://www.gotw.ca/publications/c_family_interview.htm +RedirectTemp /16-21 https://doc.rust-lang.org/std/ops/index.html +RedirectTemp /16-22 https://www.fluentpython.com/lingo/#lazy +############################################################ 17 +RedirectTemp /17-1 http://www.paulgraham.com/icad.html +RedirectTemp /17-2 https://en.wikipedia.org/wiki/Sentinel_value +RedirectTemp /17-3 https://docs.python.org/3.10/library/functions.html#iter +RedirectTemp /17-4 https://docs.python.org/3.10/library/functions.html#iter +RedirectTemp /17-5 https://github.com/python/cpython/blob/b1930bf75f276cd7ca08c4455298128d89adf7d1/Lib/_collections_abc.py#L271 +RedirectTemp /17-6 https://github.com/python/cpython/blob/main/Lib/types.py#L6 +RedirectTemp /17-7 https://en.wikipedia.org/wiki/CLU_(programming_language) +RedirectTemp /17-8 https://docs.python.org/3/glossary.html +RedirectTemp /17-9 https://docs.python.org/3/glossary.html#term-generator-iterator +RedirectTemp /17-10 https://docs.python.org/3/glossary.html#term-generator-expression +RedirectTemp /17-11 https://marc.info/?l=python-list&m=141826925106951&w=2 +RedirectTemp /17-12 https://docs.python.org/3/library/os.html#os.walk +RedirectTemp /17-13 https://docs.python.org/3/library/itertools.html +RedirectTemp /17-14 https://docs.python.org/3/library/exceptions.html#exception-hierarchy +RedirectTemp /17-15 https://en.wikipedia.org/wiki/Depth-first_search +RedirectTemp /17-16 https://docs.python.org/3.10/library/typing.html#typing.TypeAlias +RedirectTemp /17-17 https://docs.python.org/3/library/typing.html#typing.Generator +RedirectTemp /17-18 http://www.dabeaz.com/coroutines/Coroutines.pdf +RedirectTemp /17-19 http://www.dabeaz.com/coroutines/Coroutines.pdf +RedirectTemp /17-20 https://mail.python.org/pipermail/python-ideas/2009-April/003841.html +RedirectTemp /17-21 https://mail.python.org/pipermail/python-ideas/2009-April/003912.html +RedirectTemp /17-22 https://docs.python.org/3/library/exceptions.html#StopIteration +RedirectTemp /17-23 https://docs.python.org/3/reference/expressions.html#yield-expressions +RedirectTemp /17-24 https://docs.python.org/3/reference/index.html +RedirectTemp /17-25 https://github.com/python/cpython/blob/6f743e7a4da904f61dfa84cc7d7385e4dcc79ac5/Lib/typing.py#L2060 +RedirectTemp /17-26 http://catb.org/~esr/jargon/html/G/grok.html +RedirectTemp /17-27 https://docs.python.org/3/reference/expressions.html#yieldexpr +RedirectTemp /17-28 https://docs.python.org/3/library/itertools.html +RedirectTemp /17-29 https://docs.python.org/3/library/itertools.html#itertools-recipes +RedirectTemp /17-30 https://more-itertools.readthedocs.io/en/stable/index.html +RedirectTemp /17-31 https://rittau.org/2006/11/java-iterators-are-not-iterable/ +RedirectTemp /17-32 https://docs.python.org/3/whatsnew/3.3.html#pep-380-syntax-for-delegating-to-a-subgenerator +RedirectTemp /17-33 http://www.dabeaz.com/generators/ +RedirectTemp /17-34 http://www.dabeaz.com/coroutines/ +RedirectTemp /17-35 https://archive.org/details/pyvideo_213___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-1-of-3 +RedirectTemp /17-36 https://archive.org/details/pyvideo_215___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-2-of-3 +RedirectTemp /17-37 https://archive.org/details/pyvideo_214___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-3-of-3 +RedirectTemp /17-38 http://www.dabeaz.com/finalgenerator/ +RedirectTemp /17-39 https://web.archive.org/web/20200218150637/http://seriously.dontusethiscode.com/2013/05/01/greedy-coroutine.html +RedirectTemp /17-40 https://effectivepython.com/ +RedirectTemp /17-41 https://effectivepython.com/2015/03/10/consider-coroutines-to-run-many-functions-concurrently +RedirectTemp /17-42 https://en.wikipedia.org/wiki/Conway's_Game_of_Life +RedirectTemp /17-43 https://gist.github.com/ramalho/da5590bc38c973408839 +RedirectTemp /17-44 https://gist.github.com/ramalho/da5590bc38c973408839 +RedirectTemp /17-45 https://journal.code4lib.org/articles/4893 +RedirectTemp /17-46 https://github.com/fluentpython/isis2json +RedirectTemp /17-47 https://github.com/fluentpython/isis2json/blob/master/README.rst +############################################################ 18 +RedirectTemp /18-1 https://pyvideo.org/video/1669/keynote-3/ +RedirectTemp /18-2 https://docs.python.org/3/library/sqlite3.html#using-the-connection-as-a-context-manager +RedirectTemp /18-3 https://docs.python.org/3/library/threading.html#using-locks-conditions-and-semaphores-in-the-with-statement +RedirectTemp /18-4 https://docs.python.org/3/library/decimal.html#decimal.localcontext +RedirectTemp /18-5 https://docs.python.org/3/library/unittest.mock.html#patch +RedirectTemp /18-6 https://docs.python.org/3/library/contextlib.html#contextlib.redirect_stdout +RedirectTemp /18-7 https://docs.python.org/3/library/sys.html#sys.exc_info +RedirectTemp /18-8 https://en.wikipedia.org/wiki/LL_parser +RedirectTemp /18-9 https://docs.python.org/3/library/contextlib.html +RedirectTemp /18-10 https://github.com/python/cpython/blob/8afab2ebbc1b343cd88d058914cf622fe687a2be/Lib/contextlib.py#L123 +RedirectTemp /18-11 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ +RedirectTemp /18-12 https://docs.python.org/3/library/fileinput.html#fileinput.input +RedirectTemp /18-13 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ +RedirectTemp /18-14 https://en.wikipedia.org/wiki/Euclidean_algorithm +RedirectTemp /18-15 https://github.com/fluentpython/example-code-2e/tree/master/18-with-match/lispy/py3.10/ +RedirectTemp /18-16 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lispy.py +RedirectTemp /18-17 https://github.com/fluentpython/example-code-2e/blob/6527037ae7319ba370a1ee2d9fe79214d0ed9452/18-with-match/lispy/py3.10/lis.py#L35 +RedirectTemp /18-18 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/examples_test.py +RedirectTemp /18-19 https://github.com/python/typeshed/issues/6042 +RedirectTemp /18-20 https://github.com/fluentpython/lispy/tree/main/mylis +RedirectTemp /18-21 https://github.com/fluentpython/example-code-2e/blob/00e4741926e1b771ee7c753148b1415c0bd12e39/02-array-seq/lispy/py3.10/examples_test.py +RedirectTemp /18-22 https://mitpress.mit.edu/sites/default/files/sicp/index.html +RedirectTemp /18-23 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/examples_test.py +RedirectTemp /18-24 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/lis.py +RedirectTemp /18-25 https://www.python.org/dev/peps/pep-0634/#or-patterns +RedirectTemp /18-26 https://en.wikipedia.org/wiki/Lambda#Character_encodings +RedirectTemp /18-27 https://docs.python.org/3/reference/compound_stmts.html +RedirectTemp /18-28 https://docs.python.org/3/glossary.html#term-eafp +RedirectTemp /18-29 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 +RedirectTemp /18-30 https://docs.python.org/3/reference/compound_stmts.html +RedirectTemp /18-31 https://stackoverflow.com/questions/16138232/is-it-a-good-practice-to-use-try-except-else-in-python +RedirectTemp /18-32 https://docs.python.org/3/library/stdtypes.html#typecontextmanager +RedirectTemp /18-33 https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers +RedirectTemp /18-34 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 +RedirectTemp /18-35 https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger-1?slide=34 +RedirectTemp /18-36 https://preshing.com/20110920/the-python-with-statement-by-example/ +RedirectTemp /18-37 https://www.rath.org/on-the-beauty-of-pythons-exitstack.html +RedirectTemp /18-38 https://norvig.com/lispy.html +RedirectTemp /18-39 https://norvig.com/lispy2.html +RedirectTemp /18-40 https://github.com/norvig/pytudes +RedirectTemp /18-41 https://github.com/fluentpython/lispy +RedirectTemp /18-42 https://racket-lang.org/ +RedirectTemp /18-43 https://pyvideo.org/video/1669/keynote-3/ +RedirectTemp /18-44 https://en.wikipedia.org/wiki/Tail_call +RedirectTemp /18-45 https://2ality.com/2015/06/tail-call-optimization.html +RedirectTemp /18-46 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lis.py +RedirectTemp /18-47 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lispy.py +RedirectTemp /18-48 http://neopythonic.blogspot.com/2009/04/final-words-on-tail-calls.html +RedirectTemp /18-49 https://webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit/ +RedirectTemp /18-50 http://kangax.github.io/compat-table/es6/ +RedirectTemp /18-51 https://world.hey.com/mgmarlow/what-happened-to-proper-tail-calls-in-javascript-5494c256 +RedirectTemp /18-52 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html +RedirectTemp /18-53 https://github.com/fluentpython/lispy/blob/main/mylis/mylis_2/lis.py +RedirectTemp /18-54 https://github.com/fluentpython/lispy/tree/main/mylis +############################################################ 19 +RedirectTemp /19-1 https://go.dev/blog/waza-talk +RedirectTemp /19-2 https://en.wikipedia.org/wiki/Graphics_processing_unit +RedirectTemp /19-3 https://docs.python.org/3/library/sys.html#sys.getswitchinterval +RedirectTemp /19-4 https://docs.python.org/3/library/sys.html#sys.setswitchinterval +RedirectTemp /19-5 https://en.wikipedia.org/wiki/System_call +RedirectTemp /19-6 https://mail.python.org/pipermail/python-dev/2009-October/093356.html +RedirectTemp /19-7 http://www.dabeaz.com/finalgenerator/ +RedirectTemp /19-8 https://docs.python.org/3/library/threading.html#thread-objects +RedirectTemp /19-9 https://www.pypy.org/ +RedirectTemp /19-10 https://mail.python.org/pipermail/python-list/2009-February/675659.html +RedirectTemp /19-11 https://en.wikipedia.org/wiki/Braille_Patterns +RedirectTemp /19-12 https://docs.python.org/3/library/multiprocessing.shared_memory.html +RedirectTemp /19-13 https://docs.python.org/3/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.ShareableList +RedirectTemp /19-14 https://greenlet.readthedocs.io/en/latest/ +RedirectTemp /19-15 https://docs.sqlalchemy.org/en/14/changelog/migration_14.html#asynchronous-io-support-for-core-and-orm +RedirectTemp /19-16 https://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html +RedirectTemp /19-17 http://www.gevent.org/ +RedirectTemp /19-18 https://github.com/gevent/gevent/wiki/Projects +RedirectTemp /19-19 https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor-example +RedirectTemp /19-20 https://github.com/python/asyncio/issues/284 +RedirectTemp /19-21 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor +RedirectTemp /19-22 https://mail.python.org/archives/list/python-dev@python.org/message/JBYXQH3NV3YBF7P2HLHB5CD6V3GVTY55/ +RedirectTemp /19-23 https://docs.python.org/3/library/queue.html#queue.SimpleQueue.get +RedirectTemp /19-24 https://en.wikipedia.org/wiki/Race_condition +RedirectTemp /19-25 https://github.com/fluentpython/example-code-2e/commit/2c1230579db99738a5e5e6802063bda585f6476d +RedirectTemp /19-26 https://github.com/fluentpython/example-code-2e/blob/master/19-concurrency/primes/README.md +RedirectTemp /19-27 https://github.com/fluentpython/example-code-2e/blob/master/19-concurrency/primes/threads.py +RedirectTemp /19-28 https://en.wikipedia.org/wiki/Context_switch +RedirectTemp /19-29 http://www.gotw.ca/publications/concurrency-ddj.htm +RedirectTemp /19-30 https://www.ansible.com/ +RedirectTemp /19-31 https://saltproject.io/ +RedirectTemp /19-32 https://www.fabfile.org/ +RedirectTemp /19-33 https://engineering.fb.com/2016/05/27/production-engineering/python-in-production-engineering/ +RedirectTemp /19-34 https://jupyter.org/ +RedirectTemp /19-35 https://docs.bokeh.org/en/latest/index.html +RedirectTemp /19-36 https://www.tensorflow.org/ +RedirectTemp /19-37 https://pytorch.org/ +RedirectTemp /19-38 https://www.oreilly.com/radar/where-programming-ops-ai-and-the-cloud-are-headed-in-2021/ +RedirectTemp /19-39 https://www.youtube.com/watch?v=ods97a5Pzw0 +RedirectTemp /19-40 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy +RedirectTemp /19-41 https://modwsgi.readthedocs.io/en/master/ +RedirectTemp /19-42 https://uwsgi-docs.readthedocs.io/en/latest/ +RedirectTemp /19-43 https://unit.nginx.org/ +RedirectTemp /19-44 https://www.techatbloomberg.com/blog/configuring-uwsgi-production-deployment/ +RedirectTemp /19-45 https://www.youtube.com/watch?v=p6R1h2Nn468 +RedirectTemp /19-46 https://asgi.readthedocs.io/en/latest/index.html +RedirectTemp /19-47 https://docs.celeryproject.org/en/stable/getting-started/introduction.html +RedirectTemp /19-48 https://python-rq.org/ +RedirectTemp /19-49 https://docs.celeryproject.org/en/stable/faq.html#what-kinds-of-things-should-i-use-celery-for +RedirectTemp /19-50 https://redis.io/ +RedirectTemp /19-51 https://realpython.com/intro-to-python-threading/ +RedirectTemp /19-52 https://pymotw.com/3/concurrency.html +RedirectTemp /19-53 https://www.pearson.com/us/higher-education/program/Hellmann-Python-3-Standard-Library-by-Example-The/PGM328871.html +RedirectTemp /19-54 https://docs.python.org/3/library/multiprocessing.html#programming-guidelines +RedirectTemp /19-55 https://docs.python.org/3/library/multiprocessing.html +RedirectTemp /19-56 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ +RedirectTemp /19-57 https://link.springer.com/book/10.1007/978-1-4842-5793-7?error=cookies_not_supported&code=2ed5d61d-ae9f-4f3d-94ac-0f68cf45ea4f +RedirectTemp /19-58 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 +RedirectTemp /19-59 https://greenteapress.com/wp/semaphores/ +RedirectTemp /19-60 https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock +RedirectTemp /19-61 https://docs.python.org/3/faq/library.html#can-t-we-get-rid-of-the-global-interpreter-lock +RedirectTemp /19-62 https://www.artima.com/weblogs/viewpost.jsp?thread=214235 +RedirectTemp /19-63 http://jessenoller.com/blog/2009/02/01/python-threads-and-the-global-interpreter-lock +RedirectTemp /19-64 https://realpython.com/products/cpython-internals-book/ +RedirectTemp /19-65 http://www.dabeaz.com/GIL/ +RedirectTemp /19-66 http://www.dabeaz.com/python/UnderstandingGIL.pdf +RedirectTemp /19-67 https://bugs.python.org/issue7946#msg223110 +RedirectTemp /19-68 https://bugs.python.org/issue7946 +RedirectTemp /19-69 https://www.fullstackpython.com/ +RedirectTemp /19-70 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ +RedirectTemp /19-71 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 +RedirectTemp /19-72 https://www.packtpub.com/product/distributed-computing-with-python/9781785889691 +RedirectTemp /19-73 https://towardsdatascience.com/python-performance-and-gpus-1be860ffd58d?gi=a7d537cc2fb4 +RedirectTemp /19-74 https://instagram-engineering.com/web-service-efficiency-at-instagram-with-python-4976d078e366?gi=12a441991c88 +RedirectTemp /19-75 https://www.oreilly.com/library/view/architecture-patterns-with/9781492052197/ +RedirectTemp /19-76 https://www.cosmicpython.com/ +RedirectTemp /19-77 https://pypi.org/project/lelo/ +RedirectTemp /19-78 https://github.com/npryce/python-parallelize +RedirectTemp /19-79 https://github.com/ericsnowcurrently/multi-core-python/wiki +RedirectTemp /19-80 https://gist.github.com/markshannon/79cace3656b40e21b7021504daee950c +RedirectTemp /19-81 https://mail.python.org/archives/list/python-dev@python.org/message/YOOQZCFOKEPQ24YHWWLQSJ3RCXFMS7D7/ +RedirectTemp /19-82 https://en.wikipedia.org/wiki/Communicating_sequential_processes +RedirectTemp /19-83 https://github.com/stackless-dev/stackless/wiki +RedirectTemp /19-84 https://www.eveonline.com +RedirectTemp /19-85 https://www.ccpgames.com/ +RedirectTemp /19-86 https://stackless.readthedocs.io/en/3.6-slp/stackless-python.html#history +RedirectTemp /19-87 https://doc.pypy.org/en/latest/stackless.html +RedirectTemp /19-88 https://greenlet.readthedocs.io/en/latest/ +RedirectTemp /19-89 http://www.gevent.org/ +RedirectTemp /19-90 http://thespianpy.com/doc/ +RedirectTemp /19-91 https://pykka.readthedocs.io/en/latest/ +RedirectTemp /19-92 https://www.manning.com/books/rabbitmq-in-action +RedirectTemp /19-93 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ +RedirectTemp /19-94 https://en.wikipedia.org/wiki/OpenCL +RedirectTemp /19-95 https://media.pragprog.com/titles/pb7con/Bonus_Chapter.pdf +RedirectTemp /19-96 https://martinfowler.com/ +RedirectTemp /19-97 https://martinfowler.com/articles/patterns-of-distributed-systems/ +RedirectTemp /19-98 https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/ +RedirectTemp /19-99 https://www.oreilly.com/library/view/oscon-2016-video/9781491965153/video247021.html +RedirectTemp /19-100 https://www.oreilly.com/library/view/designing-for-scalability/9781449361556/ +RedirectTemp /19-101 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy +RedirectTemp /19-102 https://en.wikipedia.org/wiki/KISS_principle +RedirectTemp /19-103 https://www.usenix.org/conference/hotos15/workshop-program/presentation/mcsherry +############################################################ 20 +RedirectTemp /20-1 https://www.artima.com/weblogs/viewpost.jsp?thread=299551 +RedirectTemp /20-2 https://docs.python.org/3/library/http.server.html +RedirectTemp /20-3 https://www.youtube.com/watch?v=A9e9Cy1UkME +RedirectTemp /20-4 https://www.cia.gov/the-world-factbook/ +RedirectTemp /20-5 https://docs.python-requests.org/en/latest/ +RedirectTemp /20-6 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor +RedirectTemp /20-7 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed +RedirectTemp /20-8 https://docs.python.org/3/library/concurrent.futures.html +RedirectTemp /20-9 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.Executor +RedirectTemp /20-10 https://github.com/fluentpython/example-code-2e/blob/master/20-executors/getflags/flags2_common.py +RedirectTemp /20-11 https://github.com/noamraph/tqdm +RedirectTemp /20-12 https://www.youtube.com/watch?v=M8Z65tAl5l4 +RedirectTemp /20-13 https://github.com/noamraph/tqdm/blob/master/README.md +RedirectTemp /20-14 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed +RedirectTemp /20-15 https://docs.python.org/3/library/asyncio-task.html#asyncio.as_completed +RedirectTemp /20-16 https://www.cloudflare.com/ +RedirectTemp /20-17 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags +RedirectTemp /20-18 https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418 +RedirectTemp /20-19 https://en.wikipedia.org/wiki/Embarrassingly_parallel +RedirectTemp /20-20 https://pyvideo.org/video/480/pyconau-2010--the-future-is-soon/ +RedirectTemp /20-21 http://www.dabeaz.com/coroutines/ +RedirectTemp /20-22 https://en.wikipedia.org/wiki/POSIX_Threads +RedirectTemp /20-23 https://en.wikipedia.org/wiki/C_dynamic_memory_allocation +RedirectTemp /20-24 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ +RedirectTemp /20-25 https://hexdocs.pm/ecto/getting-started.html +############################################################ 21 +RedirectTemp /21-1 https://docs.python.org/3/library/asyncio.html +RedirectTemp /21-2 https://bugs.python.org/issue43216 +RedirectTemp /21-3 https://bugs.python.org/issue36921 +RedirectTemp /21-4 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.getaddrinfo +RedirectTemp /21-5 https://docs.python.org/3/library/socket.html#socket.getaddrinfo +RedirectTemp /21-6 https://docs.python.org/3.10/library/asyncio-eventloop.html#asyncio.get_event_loop +RedirectTemp /21-7 https://www.python.org/dev/peps/pep-0492/#await-expression +RedirectTemp /21-8 https://www.fluentpython.com/extra/classic-coroutines/#yield_from_meaning_sec +RedirectTemp /21-9 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags +RedirectTemp /21-10 https://magicstack.github.io/asyncpg/current/ +RedirectTemp /21-11 https://magicstack.github.io/asyncpg/current/api/index.html#transactions +RedirectTemp /21-12 https://magicstack.github.io/asyncpg/current/api/index.html#transactions +RedirectTemp /21-13 https://github.com/MagicStack/asyncpg/blob/4d39a05268ce4cc01b00458223a767542da048b8/asyncpg/transaction.py#L57 +RedirectTemp /21-14 https://magicstack.github.io/asyncpg/current/usage.html#connection-pools +RedirectTemp /21-15 https://gist.github.com/jboner/2841832 +RedirectTemp /21-16 https://en.wikipedia.org/wiki/Network-attached_storage +RedirectTemp /21-17 https://en.wikipedia.org/wiki/Semaphore_(programming) +RedirectTemp /21-18 https://en.wikipedia.org/wiki/Semaphore_(programming) +RedirectTemp /21-19 https://groups.google.com/forum/#!msg/python-tulip/PdAEtwpaJHs/7fqb-Qj2zJoJ +RedirectTemp /21-20 https://web.archive.org/web/20151209151711/http://tritarget.org/blog/2012/11/28/the-pyramid-of-doom-a-javascript-style-trap +RedirectTemp /21-21 https://stackoverflow.com/questions/53701841/what-is-the-use-case-for-future-add-done-callback/53710563 +RedirectTemp /21-22 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor +RedirectTemp /21-23 https://motor.readthedocs.io/en/stable/ +RedirectTemp /21-24 https://emptysqua.re/blog/response-to-asynchronous-python-and-databases/ +RedirectTemp /21-25 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams +RedirectTemp /21-26 https://en.wikipedia.org/wiki/Phaistos_Disc +RedirectTemp /21-27 https://en.wikipedia.org/wiki/Inverted_index +RedirectTemp /21-28 https://fastapi.tiangolo.com/ +RedirectTemp /21-29 https://swagger.io/specification/ +RedirectTemp /21-30 https://asgi.readthedocs.io/en/latest/implementations.html +RedirectTemp /21-31 https://pydantic-docs.helpmanual.io/ +RedirectTemp /21-32 https://doc.traefik.io/traefik/ +RedirectTemp /21-33 https://fastapi.tiangolo.com/project-generation/ +RedirectTemp /21-34 https://fastapi.tiangolo.com/tutorial/response-model/ +RedirectTemp /21-35 https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server +RedirectTemp /21-36 https://github.com/python/typeshed/issues/5535 +RedirectTemp /21-37 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Server.serve_forever +RedirectTemp /21-38 https://docs.python.org/3/library/asyncio-stream.html#asyncio.StreamWriter.close +RedirectTemp /21-39 https://docs.python.org/3/library/asyncio-stream.html#streamwriter +RedirectTemp /21-40 https://docs.python.org/3/library/asyncio-stream.html +RedirectTemp /21-41 https://docs.python.org/3/library/asyncio-protocol.html +RedirectTemp /21-42 https://docs.python.org/3/library/asyncio-protocol.html#tcp-echo-server +RedirectTemp /21-43 https://github.com/aio-libs/aiopg +RedirectTemp /21-44 https://docs.python.org/3/whatsnew/3.8.html#asyncio +RedirectTemp /21-45 https://datatracker.ietf.org/doc/html/rfc6761 +RedirectTemp /21-46 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager +RedirectTemp /21-47 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager +RedirectTemp /21-48 https://docs.python.org/3/library/asyncio-task.html#asyncio.gather +RedirectTemp /21-49 https://curio.readthedocs.io/en/latest/index.html +RedirectTemp /21-50 https://curio.readthedocs.io/en/latest/reference.html#task-groups +RedirectTemp /21-51 https://en.wikipedia.org/wiki/Structured_concurrency +RedirectTemp /21-52 https://mail.python.org/archives/list/python-dev@python.org/thread/2ORDAW74LGE3ZI2QETPJRT2ZL7MCCPG2/ +RedirectTemp /21-53 https://www.python.org/dev/peps/pep-0654/#motivation +RedirectTemp /21-54 https://curio.readthedocs.io/en/latest/reference.html#AWAIT +RedirectTemp /21-55 https://www.python-httpx.org/async/#curio +RedirectTemp /21-56 https://github.com/dabeaz/curio/tree/78bca8a6ad677ef51e1568ac7b3e51441ab49c42/examples +RedirectTemp /21-57 https://datatracker.ietf.org/doc/html/rfc8305 +RedirectTemp /21-58 https://trio.readthedocs.io/en/stable/ +RedirectTemp /21-59 https://www.youtube.com/watch?v=M-sc73Y-zQA +RedirectTemp /21-60 https://en.wikipedia.org/wiki/Technical_debt +RedirectTemp /21-61 https://www.youtube.com/watch?v=E-1Y4kSsAFc +RedirectTemp /21-62 https://github.com/dabeaz/curio +RedirectTemp /21-63 https://trio.readthedocs.io/en/stable/ +RedirectTemp /21-64 https://curio.readthedocs.io/en/latest/#curio-university +RedirectTemp /21-65 https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/ +RedirectTemp /21-66 https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ +RedirectTemp /21-67 https://stackoverflow.com/questions/49482969/what-is-the-core-difference-between-asyncio-and-trio +RedirectTemp /21-68 https://docs.python.org/3/library/asyncio.html +RedirectTemp /21-69 https://bugs.python.org/issue33649 +RedirectTemp /21-70 https://docs.python.org/3/library/asyncio-dev.html +RedirectTemp /21-71 https://www.youtube.com/watch?v=iG6fr81xHKA +RedirectTemp /21-72 https://www.youtube.com/watch?v=F19R_M4Nay4 +RedirectTemp /21-73 https://asherman.io/projects/unsync.html +RedirectTemp /21-74 https://pyladies.com/ +RedirectTemp /21-75 https://www.youtube.com/watch?v=sW76-pRkZk8 +RedirectTemp /21-76 https://www.youtube.com/watch?v=Xbl7XjFYsN4 +RedirectTemp /21-77 https://www.youtube.com/watch?v=02CLD-42VdI +RedirectTemp /21-78 https://micropython.org/ +RedirectTemp /21-79 https://docs.micropython.org/en/latest/library/uasyncio.html +RedirectTemp /21-80 https://www.encode.io/articles/python-async-frameworks-beyond-developer-tribalism +RedirectTemp /21-81 https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ +RedirectTemp /21-82 https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ +RedirectTemp /21-83 https://github.com/MagicStack/uvloop +RedirectTemp /21-84 http://magic.io/blog/uvloop-blazing-fast-python-networking/ +RedirectTemp /21-85 https://github.com/MagicStack/httptools +RedirectTemp /21-86 https://docs.aiohttp.org/en/stable/ +RedirectTemp /21-87 https://github.com/wg/wrk +RedirectTemp /21-88 https://twistedmatrix.com/trac/ +############################################################ 22 +RedirectTemp /22-1 https://github.com/fluentpython/example-code-2e/blob/master/22-dyn-attr-prop/oscon/data/osconfeed.json +RedirectTemp /22-2 https://pypi.org/project/attrdict/ +RedirectTemp /22-3 https://pypi.org/project/addict/ +RedirectTemp /22-4 https://github.com/ActiveState/code/tree/master/recipes/Python/52308_simple_but_handy_collector_bunch_named_stuff +RedirectTemp /22-5 https://docs.python.org/3/library/types.html#types.SimpleNamespace +RedirectTemp /22-6 https://docs.python.org/3/library/argparse.html#argparse.Namespace +RedirectTemp /22-7 https://docs.python.org/3/library/multiprocessing.html#multiprocessing.managers.Namespace +RedirectTemp /22-8 https://github.com/fluentpython/example-code-2e/blob/master/22-dyn-attr-prop/oscon/schedule_v2.py +RedirectTemp /22-9 https://docs.python.org/3/library/functools.html#functools.cached_property +RedirectTemp /22-10 https://docs.python.org/3/library/functools.html#functools.cached_property +RedirectTemp /22-11 https://bugs.python.org/issue42781 +RedirectTemp /22-12 https://docs.python.org/3/howto/descriptor.html +RedirectTemp /22-13 https://github.com/python/cpython/blob/e6d0107e13ed957109e79b796984d3d026a8660d/Lib/functools.py#L926 +RedirectTemp /22-14 https://docs.python.org/3/library/threading.html#rlock-objects +RedirectTemp /22-15 https://docs.python.org/3.10/library/functools.html#functools.cached_property +RedirectTemp /22-16 https://www.wsj.com/articles/SB10001424052970203914304576627102996831200 +RedirectTemp /22-17 https://www.youtube.com/watch?v=s35rVw1zskA&feature=youtu.be +RedirectTemp /22-18 https://docs.python.org/3/library/functions.html#dir +RedirectTemp /22-19 https://github.com/python/cpython/blob/19903085c3ad7a17c8047e1556c700f2eb109931/Lib/cmd.py#L214 +RedirectTemp /22-20 https://docs.python.org/3/library/functions.html#hasattr +RedirectTemp /22-21 https://docs.python.org/3.10/reference/datamodel.html#special-method-lookup +RedirectTemp /22-22 https://docs.python.org/3/library/functions.html +RedirectTemp /22-23 https://docs.python.org/3/reference/datamodel.html#customizing-attribute-access +RedirectTemp /22-24 https://docs.python.org/3/reference/datamodel.html#special-method-lookup +RedirectTemp /22-25 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /22-26 http://wiki.c2.com/?WelcomeVisitors +RedirectTemp /22-27 http://wiki.c2.com/?UniformAccessPrinciple +RedirectTemp /22-28 https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html +RedirectTemp /22-29 http://www.pingo.io/docs/ +RedirectTemp /22-30 https://www.drdobbs.com/javas-new-considered-harmful/184405016 +RedirectTemp /22-31 https://www.python.org/dev/peps/pep-0008/#class-names +############################################################ 23 +RedirectTemp /23-1 http://www.aleax.it/goo_pydp.pdf +RedirectTemp /23-2 https://docs.python.org/3.10/reference/datamodel.html#implementing-descriptors +RedirectTemp /23-3 https://docs.python.org/3/howto/descriptor.html +RedirectTemp /23-4 https://docs.python.org/3/howto/ +RedirectTemp /23-5 http://www.aleax.it/Python/nylug05_om.pdf +RedirectTemp /23-6 https://www.youtube.com/watch?v=VOzvpHoYQoo +RedirectTemp /23-7 https://www.python.org/dev/peps/pep-0487/#trait-descriptors +RedirectTemp /23-8 https://dreamsongs.com/RiseOfWorseIsBetter.html +RedirectTemp /23-9 http://web.archive.org/web/20031002184114/www.amk.ca/python/writing/warts.html +RedirectTemp /23-10 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this +RedirectTemp /23-11 http://python-history.blogspot.com/2009/02/adding-support-for-user-defined-classes.html +############################################################ 24 +RedirectTemp /24-1 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /24-2 https://docs.djangoproject.com/en/3.2/topics/db/models/#meta-options +RedirectTemp /24-3 https://www.python.org/dev/peps/pep-3155/ +RedirectTemp /24-4 https://github.com/python/cpython/blob/3.9/Lib/collections/__init__.py +RedirectTemp /24-5 https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions +RedirectTemp /24-6 https://go.dev/tour/basics/12 +RedirectTemp /24-7 https://bugs.python.org/issue42102 +RedirectTemp /24-8 https://docs.python.org/3/reference/datamodel.html#object.__init_subclass__ +RedirectTemp /24-9 https://www.python.org/dev/peps/pep-0557/#abstract +RedirectTemp /24-10 https://github.com/python/cpython/blob/3.9/Lib/dataclasses.py +RedirectTemp /24-11 https://docs.python.org/3/reference/datamodel.html#creating-the-class-object +RedirectTemp /24-12 https://mail.python.org/pipermail/python-list/2002-December/134521.html +RedirectTemp /24-13 https://mail.python.org/pipermail/python-list/2002-July/162558.html +RedirectTemp /24-14 https://github.com/fluentpython/example-code/tree/master/21-class-metaprog/bulkfood +RedirectTemp /24-15 https://en.wikipedia.org/wiki/Principle_of_least_astonishment +RedirectTemp /24-16 https://docs.python.org/3/reference/datamodel.html#object.__class_getitem__ +RedirectTemp /24-17 https://en.wikipedia.org/wiki/Trait_(computer_programming) +RedirectTemp /24-18 https://en.wikipedia.org/wiki/Aspect-oriented_programming +RedirectTemp /24-19 https://dhh.dk/arc/000416.html +RedirectTemp /24-20 https://github.com/cjrh/autoslot +RedirectTemp /24-21 https://docs.python.org/3/reference/datamodel.html#customizing-class-creation +RedirectTemp /24-22 https://docs.python.org/3/library/functions.html#type +RedirectTemp /24-23 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /24-24 https://docs.python.org/3/library/types.html +RedirectTemp /24-25 https://www.python.org/dev/peps/pep-3129/ +RedirectTemp /24-26 https://www.youtube.com/watch?v=cAGliEJV9_o +RedirectTemp /24-27 https://docs.python.org/3/library/functools.html#functools.total_ordering +RedirectTemp /24-28 https://www.python.org/download/releases/2.2.3/descrintro/ +RedirectTemp /24-29 https://github.com/lihaoyi/macropy +RedirectTemp /24-30 https://people.eecs.berkeley.edu/~bh/ss-toc2.html diff --git a/links/short.htaccess b/links/short.htaccess new file mode 100644 index 0000000..fbe7ddf --- /dev/null +++ b/links/short.htaccess @@ -0,0 +1 @@ +# file created and managed by short.py diff --git a/links/short.py b/links/short.py new file mode 100755 index 0000000..a9ae96c --- /dev/null +++ b/links/short.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 + +import itertools +from collections.abc import Iterator + + +def load_redirects(): + redirects = {} + targets = {} + for filename in ('custom.htaccess', 'short.htaccess'): + with open(filename) as fp: + for line in fp: + if line.startswith('RedirectTemp'): + _, short, long = line.split() + short = short[1:] # Remove leading slash + assert short not in redirects, f"{filename}: duplicate redirect from {short}" + # custom is live since 2022, we cannot change it remove duplicate targets + if not filename.startswith('custom'): + assert long not in targets, f"{filename}: Duplicate redirect to {long}" + redirects[short] = long + targets[long] = short + return redirects, targets + + +SDIGITS = '23456789abcdefghjkmnpqrstvwxyz' + + +def gen_short() -> Iterator[str]: + """ + Generate every possible sequence of SDIGITS. + """ + length = 1 + while True: + for short in itertools.product(SDIGITS, repeat=length): + yield ''.join(short) + length += 1 + + +def shorten(n: int) -> str: + """ + Get Nth short URL made from SDIGITS, where 0 is the first. + """ + iter_short = gen_short() + for i in range(n+1): + short = next(iter_short) + if i == n: + return short + + +def gen_free_short(redirects: dict) -> Iterator[str]: + """ + Generate next available short URL. + """ + for short in gen_short(): + if short not in redirects: + yield short + + +def new_urls(urls: list[str], redirects: dict, targets: dict) -> None: + iter_short = gen_free_short(redirects) + with open('short.htaccess', 'a') as fp: + for url in urls: + assert 'fpy.li' not in url, f"{url} is a fpy.li URL" + if url in targets: + continue + short = next(iter_short) + redirects[short] = url + targets[url] = short + fp.write(f"RedirectTemp /{short} {url}\n") + + +def main(): + from random import randrange + urls = [f'https://example.com/{randrange(100000)}.html' for n in range(7)] + + redirects, targets = load_redirects() + new_urls(urls, redirects, targets) + + +if __name__ == '__main__': + main() From c5490b1569c9d4d4d52f0834a5f29b81bf657529 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 04:13:41 +0000 Subject: [PATCH 120/127] build(deps): bump h11 from 0.12.0 to 0.16.0 in /21-async/mojifinder Bumps [h11](https://github.com/python-hyper/h11) from 0.12.0 to 0.16.0. - [Commits](https://github.com/python-hyper/h11/compare/v0.12.0...v0.16.0) --- updated-dependencies: - dependency-name: h11 dependency-version: 0.16.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- 21-async/mojifinder/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/21-async/mojifinder/requirements.txt b/21-async/mojifinder/requirements.txt index f7de911..6831fee 100644 --- a/21-async/mojifinder/requirements.txt +++ b/21-async/mojifinder/requirements.txt @@ -1,6 +1,6 @@ click==7.1.2 fastapi==0.65.2 -h11==0.12.0 +h11==0.16.0 pydantic==1.10.13 starlette==0.40.0 typing-extensions==3.7.4.3 From cc4e26c67a4fbe7a84094b7d1c6fffe3fddc1f45 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 22 May 2025 02:18:34 -0300 Subject: [PATCH 121/127] Remove redundant if from short.py --- links/short.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/links/short.py b/links/short.py index a9ae96c..0e95b55 100755 --- a/links/short.py +++ b/links/short.py @@ -41,10 +41,9 @@ def shorten(n: int) -> str: Get Nth short URL made from SDIGITS, where 0 is the first. """ iter_short = gen_short() - for i in range(n+1): + for _ in range(n+1): short = next(iter_short) - if i == n: - return short + return short def gen_free_short(redirects: dict) -> Iterator[str]: From 648e9f6394c753730f48587ce70e0c5d0cffdf92 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 22 May 2025 10:05:54 -0300 Subject: [PATCH 122/127] Update short.py to return list of URL substitutions --- links/short.py | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/links/short.py b/links/short.py index 0e95b55..89464a3 100755 --- a/links/short.py +++ b/links/short.py @@ -36,16 +36,6 @@ def gen_short() -> Iterator[str]: length += 1 -def shorten(n: int) -> str: - """ - Get Nth short URL made from SDIGITS, where 0 is the first. - """ - iter_short = gen_short() - for _ in range(n+1): - short = next(iter_short) - return short - - def gen_free_short(redirects: dict) -> Iterator[str]: """ Generate next available short URL. @@ -55,17 +45,23 @@ def gen_free_short(redirects: dict) -> Iterator[str]: yield short -def new_urls(urls: list[str], redirects: dict, targets: dict) -> None: +def shorten(urls: list[str], redirects: dict, targets: dict) -> list[tuple[str,str]]: + """return (short, long) pairs, updating short.htaccess as needed""' iter_short = gen_free_short(redirects) + pairs = [] with open('short.htaccess', 'a') as fp: - for url in urls: - assert 'fpy.li' not in url, f"{url} is a fpy.li URL" - if url in targets: - continue - short = next(iter_short) - redirects[short] = url - targets[url] = short - fp.write(f"RedirectTemp /{short} {url}\n") + for long in urls: + assert 'fpy.li' not in long, f"{long} is a fpy.li URL" + if long in targets: + short = targets[long] + else: + short = next(iter_short) + redirects[short] = url + targets[url] = short + fp.write(f"RedirectTemp /{short} {url}\n") + pairs.append((short, long)) + + return pairs def main(): @@ -73,7 +69,8 @@ def main(): urls = [f'https://example.com/{randrange(100000)}.html' for n in range(7)] redirects, targets = load_redirects() - new_urls(urls, redirects, targets) + for short, long in shorten(urls, redirects, targets): + print(f'fpy.li/{short}\t{long}') if __name__ == '__main__': From 5b743b5bd7040cd5c29bf2fa4804dc734ba010bd Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 22 May 2025 13:24:50 -0300 Subject: [PATCH 123/127] short.py now reads files and stdin --- links/sample-urls.txt | 47 ++++++++++++++++++++++++++++++++++++++++ links/short.htaccess | 2 +- links/short.py | 50 +++++++++++++++++++++++++++++-------------- 3 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 links/sample-urls.txt diff --git a/links/sample-urls.txt b/links/sample-urls.txt new file mode 100644 index 0000000..9eac47c --- /dev/null +++ b/links/sample-urls.txt @@ -0,0 +1,47 @@ +https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ +https://dask.org/ +http://example.com/1572039572038573208 +http://www.unicode.org/ +https://www.techcrunch.com/2024/startup-funding-trends +https://blog.medium.com/writing-tips-for-beginners +https://github.com/microsoft/typescript +https://stackoverflow.com/questions/javascript-async-await +https://www.reddit.com/r/programming/hot +https://docs.google.com/spreadsheets/create +https://www.youtube.com/watch?v=dQw4w9WgXcQ +https://www.amazon.com/dp/B08N5WRWNW +https://support.apple.com/iphone-setup-guide +https://www.wikipedia.org/wiki/Machine_Learning +https://www.linkedin.com/in/johndoe123 +https://www.instagram.com/p/CxYz123AbC/ +https://twitter.com/elonmusk/status/1234567890 +https://www.facebook.com/events/987654321 +https://drive.google.com/file/d/1AbCdEfGhIjKlMnOp/view +https://www.dropbox.com/s/qwerty123/document.pdf +https://zoom.us/j/1234567890?pwd=abcdef +https://calendly.com/janedoe/30min-meeting +https://www.shopify.com/admin/products/new +https://stripe.com/docs/api/charges/create +https://www.paypal.com/invoice/create +https://mailchimp.com/campaigns/dashboard +https://analytics.google.com/analytics/web/ +https://console.aws.amazon.com/s3/buckets +https://portal.azure.com/dashboard +https://www.figma.com/file/AbCdEf123456/design-system +https://www.notion.so/workspace/project-notes +https://trello.com/b/AbCdEfGh/marketing-board +https://slack.com/app_redirect?channel=general +https://discord.gg/AbCdEfGh123 +https://www.twitch.tv/streamername/videos +https://www.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M +https://www.netflix.com/browse/genre/83 +https://www.hulu.com/series/breaking-bad-2008 +https://www.airbnb.com/rooms/12345678 +https://www.booking.com/hotel/us/grand-plaza.html +https://www.expedia.com/flights/search?trip=roundtrip +https://www.uber.com/ride/request +https://www.doordash.com/store/pizza-palace-123 +https://www.grubhub.com/restaurant/tacos-el-rey-456 +https://www.zillow.com/homes/for_sale/San-Francisco-CA +https://www.craigslist.org/about/sites +https://www.python.org/dev/peps/pep-0484/ \ No newline at end of file diff --git a/links/short.htaccess b/links/short.htaccess index fbe7ddf..6b09a50 100644 --- a/links/short.htaccess +++ b/links/short.htaccess @@ -1 +1 @@ -# file created and managed by short.py +# content of short.htaccess file created and managed by short.py diff --git a/links/short.py b/links/short.py index 89464a3..a1b6664 100755 --- a/links/short.py +++ b/links/short.py @@ -1,8 +1,23 @@ #!/usr/bin/env python3 +""" +short.py generates unique short URLs. + +This script reads lines from stdin or files named as arguments, then: + +1. retrieves or creates new short URLs, taking into account existing RedirectTemp + directives in custom.htacess or short.htacess; +2. appends RedirectTemp directives for newly created short URLs to short.htacess; +3. outputs the list of (short, long) URLs retrieved or created. + +""" + +import fileinput import itertools from collections.abc import Iterator +from time import strftime +BASE_DOMAIN = 'fpy.li' def load_redirects(): redirects = {} @@ -25,52 +40,55 @@ def load_redirects(): SDIGITS = '23456789abcdefghjkmnpqrstvwxyz' -def gen_short() -> Iterator[str]: +def gen_short(start_len=1) -> Iterator[str]: """ - Generate every possible sequence of SDIGITS. + Generate every possible sequence of SDIGITS, starting with start_len """ - length = 1 + length = start_len while True: for short in itertools.product(SDIGITS, repeat=length): yield ''.join(short) length += 1 -def gen_free_short(redirects: dict) -> Iterator[str]: +def gen_unused_short(redirects: dict) -> Iterator[str]: """ - Generate next available short URL. + Generate next available short URL of len >= 2. """ - for short in gen_short(): + for short in gen_short(2): if short not in redirects: yield short def shorten(urls: list[str], redirects: dict, targets: dict) -> list[tuple[str,str]]: - """return (short, long) pairs, updating short.htaccess as needed""' - iter_short = gen_free_short(redirects) + """return (short, long) pairs, appending directives to short.htaccess as needed""" + iter_short = gen_unused_short(redirects) pairs = [] + timestamp = strftime('%Y-%m-%d %H:%M:%S') with open('short.htaccess', 'a') as fp: for long in urls: - assert 'fpy.li' not in long, f"{long} is a fpy.li URL" + assert BASE_DOMAIN not in long, f"{long} is a {BASE_DOMAIN} URL" if long in targets: short = targets[long] else: short = next(iter_short) - redirects[short] = url - targets[url] = short - fp.write(f"RedirectTemp /{short} {url}\n") + redirects[short] = long + targets[long] = short + if timestamp: + fp.write(f'\n# appended: {timestamp}\n') + timestamp = None + fp.write(f'RedirectTemp /{short} {long}\n') pairs.append((short, long)) return pairs def main(): - from random import randrange - urls = [f'https://example.com/{randrange(100000)}.html' for n in range(7)] - + """read URLS from filename arguments or stdin""" + urls = [line.strip() for line in fileinput.input(encoding="utf-8")] redirects, targets = load_redirects() for short, long in shorten(urls, redirects, targets): - print(f'fpy.li/{short}\t{long}') + print(f'{BASE_DOMAIN}/{short}\t{long}') if __name__ == '__main__': From ec03da74cac0899c1f6572a8d9ed880b358494af Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 22 May 2025 13:44:46 -0300 Subject: [PATCH 124/127] short.py appends timestamps to short.htaccesss --- links/short.py | 44 ++++++++++++++++++++++---------------------- ruff.toml | 4 ++++ 2 files changed, 26 insertions(+), 22 deletions(-) create mode 100644 ruff.toml diff --git a/links/short.py b/links/short.py index a1b6664..1ebd0bd 100755 --- a/links/short.py +++ b/links/short.py @@ -6,8 +6,8 @@ This script reads lines from stdin or files named as arguments, then: 1. retrieves or creates new short URLs, taking into account existing RedirectTemp - directives in custom.htacess or short.htacess; -2. appends RedirectTemp directives for newly created short URLs to short.htacess; + directives in custom.htaccess or short.htaccess; +2. appends RedirectTemp directives for newly created short URLs to short.htaccess; 3. outputs the list of (short, long) URLs retrieved or created. """ @@ -17,21 +17,25 @@ from collections.abc import Iterator from time import strftime +HTACCESS_CUSTOM = 'custom.htaccess' +HTACCESS_SHORT = 'short.htaccess' +HTACCESS_FILES = (HTACCESS_CUSTOM, HTACCESS_SHORT) BASE_DOMAIN = 'fpy.li' -def load_redirects(): + +def load_redirects() -> tuple[dict, dict]: redirects = {} targets = {} - for filename in ('custom.htaccess', 'short.htaccess'): + for filename in HTACCESS_FILES: with open(filename) as fp: for line in fp: if line.startswith('RedirectTemp'): _, short, long = line.split() short = short[1:] # Remove leading slash - assert short not in redirects, f"{filename}: duplicate redirect from {short}" - # custom is live since 2022, we cannot change it remove duplicate targets - if not filename.startswith('custom'): - assert long not in targets, f"{filename}: Duplicate redirect to {long}" + assert short not in redirects, f'{filename}: duplicate redirect from {short}' + # htaccess.custom is live since 2022, we can't change it remove duplicate targets + if filename != HTACCESS_CUSTOM: + assert long not in targets, f'{filename}: duplicate redirect to {long}' redirects[short] = long targets[long] = short return redirects, targets @@ -41,9 +45,7 @@ def load_redirects(): def gen_short(start_len=1) -> Iterator[str]: - """ - Generate every possible sequence of SDIGITS, starting with start_len - """ + """Generate every possible sequence of SDIGITS, starting with start_len""" length = start_len while True: for short in itertools.product(SDIGITS, repeat=length): @@ -52,22 +54,20 @@ def gen_short(start_len=1) -> Iterator[str]: def gen_unused_short(redirects: dict) -> Iterator[str]: - """ - Generate next available short URL of len >= 2. - """ + """Generate next available short URL of len >= 2.""" for short in gen_short(2): if short not in redirects: yield short -def shorten(urls: list[str], redirects: dict, targets: dict) -> list[tuple[str,str]]: - """return (short, long) pairs, appending directives to short.htaccess as needed""" +def shorten(urls: list[str], redirects: dict, targets: dict) -> list[tuple[str, str]]: + """Return (short, long) pairs, appending directives to HTACCESS_SHORT as needed.""" iter_short = gen_unused_short(redirects) pairs = [] timestamp = strftime('%Y-%m-%d %H:%M:%S') - with open('short.htaccess', 'a') as fp: + with open(HTACCESS_SHORT, 'a') as fp: for long in urls: - assert BASE_DOMAIN not in long, f"{long} is a {BASE_DOMAIN} URL" + assert BASE_DOMAIN not in long, f'{long} is a {BASE_DOMAIN} URL' if long in targets: short = targets[long] else: @@ -79,16 +79,16 @@ def shorten(urls: list[str], redirects: dict, targets: dict) -> list[tuple[str,s timestamp = None fp.write(f'RedirectTemp /{short} {long}\n') pairs.append((short, long)) - + return pairs -def main(): +def main() -> None: """read URLS from filename arguments or stdin""" - urls = [line.strip() for line in fileinput.input(encoding="utf-8")] + urls = [line.strip() for line in fileinput.input(encoding='utf-8')] redirects, targets = load_redirects() for short, long in shorten(urls, redirects, targets): - print(f'{BASE_DOMAIN}/{short}\t{long}') + print(f'{BASE_DOMAIN}/{short}\t{long}') if __name__ == '__main__': diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..f7b07e2 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,4 @@ +line-length = 100 +[format] +# Like Python's repr(), use single quotes for strings. +quote-style = "single" \ No newline at end of file From cf996500070e0d712a3b088d29428f74ddc9fa1f Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Thu, 22 May 2025 14:28:42 -0300 Subject: [PATCH 125/127] minor refactoring to make it easier to call shorten() --- links/short.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/links/short.py b/links/short.py index 1ebd0bd..d67c71d 100755 --- a/links/short.py +++ b/links/short.py @@ -60,8 +60,9 @@ def gen_unused_short(redirects: dict) -> Iterator[str]: yield short -def shorten(urls: list[str], redirects: dict, targets: dict) -> list[tuple[str, str]]: +def shorten(urls: list[str]) -> list[tuple[str, str]]: """Return (short, long) pairs, appending directives to HTACCESS_SHORT as needed.""" + redirects, targets = load_redirects() iter_short = gen_unused_short(redirects) pairs = [] timestamp = strftime('%Y-%m-%d %H:%M:%S') @@ -86,8 +87,7 @@ def shorten(urls: list[str], redirects: dict, targets: dict) -> list[tuple[str, def main() -> None: """read URLS from filename arguments or stdin""" urls = [line.strip() for line in fileinput.input(encoding='utf-8')] - redirects, targets = load_redirects() - for short, long in shorten(urls, redirects, targets): + for short, long in shorten(urls): print(f'{BASE_DOMAIN}/{short}\t{long}') From 162dbadbe5cabdfc737c85446a8d788398dfe8b8 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Fri, 23 May 2025 16:35:01 -0300 Subject: [PATCH 126/127] links from vol1 to pythonfluente.com --- links/FPY.LI.htaccess | 1091 ++++++++++++++++++++++++++++++++++++++++- links/short.htaccess | 13 + 2 files changed, 1102 insertions(+), 2 deletions(-) diff --git a/links/FPY.LI.htaccess b/links/FPY.LI.htaccess index 7338157..5607fa4 100644 --- a/links/FPY.LI.htaccess +++ b/links/FPY.LI.htaccess @@ -1,2 +1,1089 @@ -# to update this file: -# cat custom.htaccess short.htaccess >> FPY.LI.htaccess +# to recreate or update this file: +# $ cat custom.htaccess short.htaccess > FPY.LI.htaccess + +ErrorDocument 404 /404.html + +# main resources +RedirectTemp /book https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ +RedirectTemp /code https://github.com/fluentpython/example-code-2e +RedirectTemp /home https://www.fluentpython.com/ + +# URLs mentioned at least three times +RedirectTemp /bisect https://www.fluentpython.com/extra/ordered-sequences-with-bisect/ +RedirectTemp /cardxvi https://www.python.org/dev/peps/pep-0484/#the-numeric-tower +RedirectTemp /collec https://docs.python.org/3/library/collections.html +RedirectTemp /dask https://dask.org/ +RedirectTemp /dtmodel https://docs.python.org/3/reference/datamodel.html +RedirectTemp /descr101 https://www.python.org/download/releases/2.2.3/descrintro/ +RedirectTemp /descrhow https://docs.python.org/3/howto/descriptor.html +RedirectTemp /doctest https://docs.python.org/3/library/doctest.html +RedirectTemp /effectpy https://effectivepython.com/ +RedirectTemp /fmtspec https://docs.python.org/3/library/string.html#formatspec +RedirectTemp /gunicorn https://gunicorn.org/ +RedirectTemp /hashint https://www.fluentpython.com/extra/internals-of-sets-and-dicts/ +RedirectTemp /hattingh https://www.oreilly.com/library/view/using-asyncio-in/9781492075325/ +RedirectTemp /httpx https://www.python-httpx.org/ +RedirectTemp /initvar https://docs.python.org/3/library/dataclasses.html#init-only-variables +RedirectTemp /mypy https://mypy.readthedocs.io/en/stable/ +RedirectTemp /norvigdp http://norvig.com/design-patterns/ +RedirectTemp /nsphere https://en.wikipedia.org/wiki/N-sphere +RedirectTemp /oldcoro https://www.fluentpython.com/extra/classic-coroutines/ +RedirectTemp /pandas https://pandas.pydata.org/ +RedirectTemp /pycook3 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ +RedirectTemp /pynut3 https://www.oreilly.com/library/view/python-in-a/9781491913833/ +RedirectTemp /pypydif https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /shed4051 https://github.com/python/typeshed/issues/4051 +RedirectTemp /specattr https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine +RedirectTemp /typing https://docs.python.org/3/library/typing.html +RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ + +# URL added during QA of the Second Edition +RedirectTemp /bdfl https://www.artima.com/weblogs/viewpost.jsp?thread=235725 + +# Python Enhancement Proposals +RedirectTemp /pep218 https://www.python.org/dev/peps/pep-0218/ +RedirectTemp /pep227 https://www.python.org/dev/peps/pep-0227/ +RedirectTemp /pep255 https://www.python.org/dev/peps/pep-0255/ +RedirectTemp /pep342 https://www.python.org/dev/peps/pep-0342/ +RedirectTemp /pep343 https://www.python.org/dev/peps/pep-0343/ +RedirectTemp /pep357 https://www.python.org/dev/peps/pep-0357/ +RedirectTemp /pep362 https://www.python.org/dev/peps/pep-0362/ +RedirectTemp /pep371 https://www.python.org/dev/peps/pep-0371/ +RedirectTemp /pep380 https://www.python.org/dev/peps/pep-0380/ +RedirectTemp /pep393 https://www.python.org/dev/peps/pep-0393/ +RedirectTemp /pep412 https://www.python.org/dev/peps/pep-0412/ +RedirectTemp /pep442 https://www.python.org/dev/peps/pep-0442/ +RedirectTemp /pep443 https://www.python.org/dev/peps/pep-0443/ +RedirectTemp /pep448 https://www.python.org/dev/peps/pep-0448/ +RedirectTemp /pep455 https://www.python.org/dev/peps/pep-0455/ +RedirectTemp /pep456 https://www.python.org/dev/peps/pep-0456/ +RedirectTemp /pep461 https://www.python.org/dev/peps/pep-0461/ +RedirectTemp /pep465 https://www.python.org/dev/peps/pep-0465/ +RedirectTemp /pep467 https://www.python.org/dev/peps/pep-0467/ +RedirectTemp /pep482 https://www.python.org/dev/peps/pep-0482/ +RedirectTemp /pep483 https://www.python.org/dev/peps/pep-0483/ +RedirectTemp /pep484 https://www.python.org/dev/peps/pep-0484/ +RedirectTemp /pep487 https://www.python.org/dev/peps/pep-0487/ +RedirectTemp /pep492 https://www.python.org/dev/peps/pep-0492/ +RedirectTemp /pep519 https://www.python.org/dev/peps/pep-0519/ +RedirectTemp /pep525 https://www.python.org/dev/peps/pep-0525/ +RedirectTemp /pep526 https://www.python.org/dev/peps/pep-0526/ +RedirectTemp /pep528 https://www.python.org/dev/peps/pep-0528/ +RedirectTemp /pep529 https://www.python.org/dev/peps/pep-0529/ +RedirectTemp /pep530 https://www.python.org/dev/peps/pep-0530/ +RedirectTemp /pep544 https://www.python.org/dev/peps/pep-0544/ +RedirectTemp /pep554 https://www.python.org/dev/peps/pep-0554/ +RedirectTemp /pep557 https://www.python.org/dev/peps/pep-0557/ +RedirectTemp /pep560 https://www.python.org/dev/peps/pep-0560/ +RedirectTemp /pep561 https://www.python.org/dev/peps/pep-0561/ +RedirectTemp /pep563 https://www.python.org/dev/peps/pep-0563/ +RedirectTemp /pep570 https://www.python.org/dev/peps/pep-0570/ +RedirectTemp /pep572 https://www.python.org/dev/peps/pep-0572/ +RedirectTemp /pep584 https://www.python.org/dev/peps/pep-0584/ +RedirectTemp /pep585 https://www.python.org/dev/peps/pep-0585/ +RedirectTemp /pep586 https://www.python.org/dev/peps/pep-0586/ +RedirectTemp /pep589 https://www.python.org/dev/peps/pep-0589/ +RedirectTemp /pep591 https://www.python.org/dev/peps/pep-0591/ +RedirectTemp /pep593 https://www.python.org/dev/peps/pep-0593/ +RedirectTemp /pep604 https://www.python.org/dev/peps/pep-0604/ +RedirectTemp /pep612 https://www.python.org/dev/peps/pep-0612/ +RedirectTemp /pep613 https://www.python.org/dev/peps/pep-0613/ +RedirectTemp /pep616 https://www.python.org/dev/peps/pep-0616/ +RedirectTemp /pep617 https://www.python.org/dev/peps/pep-0617/ +RedirectTemp /pep618 https://www.python.org/dev/peps/pep-0618/ +RedirectTemp /pep634 https://www.python.org/dev/peps/pep-0634/ +RedirectTemp /pep635 https://www.python.org/dev/peps/pep-0635/ +RedirectTemp /pep636 https://www.python.org/dev/peps/pep-0636/ +RedirectTemp /pep638 https://www.python.org/dev/peps/pep-0638/ +RedirectTemp /pep645 https://www.python.org/dev/peps/pep-0645/ +RedirectTemp /pep646 https://www.python.org/dev/peps/pep-0646/ +RedirectTemp /pep647 https://www.python.org/dev/peps/pep-0647/ +RedirectTemp /pep649 https://www.python.org/dev/peps/pep-0649/ +RedirectTemp /pep654 https://www.python.org/dev/peps/pep-0654/ +RedirectTemp /pep655 https://www.python.org/dev/peps/pep-0655/ +RedirectTemp /pep661 https://www.python.org/dev/peps/pep-0661/ +RedirectTemp /pep3099 https://www.python.org/dev/peps/pep-3099/ +RedirectTemp /pep3102 https://www.python.org/dev/peps/pep-3102/ +RedirectTemp /pep3104 https://www.python.org/dev/peps/pep-3104/ +RedirectTemp /pep3106 https://www.python.org/dev/peps/pep-3106/ +RedirectTemp /pep3107 https://www.python.org/dev/peps/pep-3107/ +RedirectTemp /pep3115 https://www.python.org/dev/peps/pep-3115/ +RedirectTemp /pep3118 https://www.python.org/dev/peps/pep-3118/ +RedirectTemp /pep3119 https://www.python.org/dev/peps/pep-3119/ +RedirectTemp /pep3129 https://www.python.org/dev/peps/pep-3129/ +RedirectTemp /pep3132 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /pep3141 https://www.python.org/dev/peps/pep-3141/ +RedirectTemp /pep3148 https://www.python.org/dev/peps/pep-3148/ +RedirectTemp /pep3155 https://www.python.org/dev/peps/pep-3155/ +RedirectTemp /pep3333 https://www.python.org/dev/peps/pep-3333/ + +# Remaining URLs by chapter + +############################################################ p +RedirectTemp /p-1 https://mail.python.org/pipermail/python-list/2002-December/134521.html +RedirectTemp /p-2 https://docs.python.org/3.10/tutorial/ +RedirectTemp /p-3 https://docs.python.org/3/tutorial/ +RedirectTemp /p-4 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ +RedirectTemp /p-5 https://www.oreilly.com/online-learning/try-now.html +RedirectTemp /p-6 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ +RedirectTemp /p-7 https://stackoverflow.com/users/95810/alex-martelli +RedirectTemp /p-8 https://pythonpro.com.br +RedirectTemp /p-9 https://groups.google.com/g/python-brasil +RedirectTemp /p-10 https://www.coffeelab.com.br/ +RedirectTemp /p-11 https://garoa.net.br/wiki/P%C3%A1gina_principal +############################################################ a +RedirectTemp /a-1 https://groups.google.com/forum/#!topic/python-tulip/Y4bhLNbKs74 +RedirectTemp /a-2 https://docs.python.org/3/library/asyncio-eventloop.html#executor +RedirectTemp /a-3 https://www.youtube.com/watch?v=x-kB2o8sd5c +RedirectTemp /a-4 https://www.youtube.com/watch?v=OSGv2VnC0go +RedirectTemp /a-5 https://mail.python.org/pipermail/python-ideas/2015-March/032557.html +RedirectTemp /a-6 https://pypi.org/project/pep8/ +RedirectTemp /a-7 https://pypi.org/project/flake8/ +RedirectTemp /a-8 https://pypi.org/project/pyflakes/ +RedirectTemp /a-9 https://pypi.org/project/mccabe/ +RedirectTemp /a-10 https://google.github.io/styleguide/pyguide.html +RedirectTemp /a-11 https://flask.palletsprojects.com/en/1.1.x/styleguide/ +RedirectTemp /a-12 https://docs.python-guide.org/ +RedirectTemp /a-13 https://david.goodger.org/projects/pycon/2007/idiomatic/handout.html +RedirectTemp /a-14 https://docs.mongodb.com/manual/about/#about-the-documentation-process +RedirectTemp /a-15 https://blog.startifact.com/posts/older/what-is-pythonic.html +RedirectTemp /a-16 https://mail.python.org/pipermail/tutor/2003-October/thread.html#25930 +RedirectTemp /a-17 https://mail.python.org/pipermail/python-list/2003-April/192027.html +RedirectTemp /a-18 https://www.python.org/doc/essays/ +############################################################ 01 +RedirectTemp /1-1 http://hugunin.net/story_of_jython.html +RedirectTemp /1-2 https://www.oreilly.com/library/view/jython-essentials/9781449397364/ +RedirectTemp /1-3 https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers +RedirectTemp /1-4 https://docs.python.org/3.10/library/string.html#format-string-syntax +RedirectTemp /1-5 https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr +RedirectTemp /1-6 https://docs.python.org/3/library/stdtypes.html#truth +RedirectTemp /1-7 https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists +RedirectTemp /1-8 https://www.python.org/doc/humor/#the-zen-of-python +RedirectTemp /1-9 https://stackoverflow.com/users/95810/alex-martelli +RedirectTemp /1-10 https://en.wikipedia.org/wiki/Object_model +RedirectTemp /1-11 https://www.dourish.com/goodies/jargon.html +RedirectTemp /1-12 https://zopeinterface.readthedocs.io/en/latest/ +RedirectTemp /1-13 https://plone.org/ +############################################################ 02 +RedirectTemp /2-1 https://github.com/fluentpython/example-code-2e/blob/master/02-array-seq/listcomp_speed.py +RedirectTemp /2-2 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /2-3 https://stackoverflow.com/questions/68630/are-tuples-more-efficient-than-lists-in-python/22140115#22140115 +RedirectTemp /2-4 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations +RedirectTemp /2-5 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations +RedirectTemp /2-6 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching +RedirectTemp /2-7 https://docs.python.org/3.10/whatsnew/3.10.html +RedirectTemp /2-8 https://en.wikipedia.org/wiki/Switch_statement#Fallthrough +RedirectTemp /2-9 https://en.wikipedia.org/wiki/Dangling_else +RedirectTemp /2-10 https://github.com/gvanrossum/patma/blob/3ece6444ef70122876fd9f0099eb9490a2d630df/EXAMPLES.md#case-6-a-very-deep-iterable-and-type-match-with-extraction +RedirectTemp /2-11 https://github.com/fluentpython/lispy/blob/main/original/norvig/lis.py +RedirectTemp /2-12 https://norvig.com/lispy.html +RedirectTemp /2-13 https://numpy.org/doc/stable/user/quickstart.html#indexing-slicing-and-iterating +RedirectTemp /2-14 https://pythontutor.com/ +RedirectTemp /2-15 https://en.wikipedia.org/wiki/Fluent_interface +RedirectTemp /2-16 https://docs.python.org/3/library/bisect.html#bisect.insort +RedirectTemp /2-17 https://stackoverflow.com/questions/4845418/when-should-a-memoryview-be-used/ +RedirectTemp /2-18 https://www.fluentpython.com/extra/parsing-binary-struct/ +RedirectTemp /2-19 http://www.netlib.org +RedirectTemp /2-20 https://pandas.pydata.org/ +RedirectTemp /2-21 https://scikit-learn.org/stable/ +RedirectTemp /2-22 https://docs.python.org/3/howto/sorting.html +RedirectTemp /2-23 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /2-24 https://bugs.python.org/issue2292 +RedirectTemp /2-25 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching +RedirectTemp /2-26 https://docs.python.org/3.10/whatsnew/3.10.html +RedirectTemp /2-27 https://www.python.org/dev/peps/pep-0636/#appendix-a-quick-intro +RedirectTemp /2-28 https://eli.thegreenplace.net/2011/11/28/less-copies-in-python-with-the-buffer-protocol-and-memoryviews/ +RedirectTemp /2-29 https://jakevdp.github.io/PythonDataScienceHandbook/ +RedirectTemp /2-30 https://www.oreilly.com/library/view/python-for-data/9781491957653/ +RedirectTemp /2-31 https://www.labri.fr/perso/nrougier/from-python-to-numpy/ +RedirectTemp /2-32 https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html +RedirectTemp /2-33 http://www.fonts101.com/fonts/view/Uncategorized/34398/Dijkstra +RedirectTemp /2-34 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types +RedirectTemp /2-35 https://en.wikipedia.org/wiki/Timsort +RedirectTemp /2-36 http://www.groklaw.net/pdf3/OraGoogle-1202.pdf +RedirectTemp /2-37 https://www.python.org/doc/humor/#id9 +############################################################ 03 +RedirectTemp /3-1 https://www.python.org/dev/peps/pep-0584/#motivation +RedirectTemp /3-2 https://docs.python.org/3.10/c-api/typeobj.html#Py_TPFLAGS_MAPPING +RedirectTemp /3-3 https://docs.python.org/3/glossary.html#term-hashable +RedirectTemp /3-4 https://docs.python.org/3/glossary.html#term-hashable +RedirectTemp /3-5 http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf +RedirectTemp /3-6 https://github.com/pingo-io/pingo-py +RedirectTemp /3-7 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/missing.py +RedirectTemp /3-8 https://docs.python.org/3/library/collections.html#collections.ChainMap +RedirectTemp /3-9 https://docs.python.org/3/library/collections.html#collections.Counter +RedirectTemp /3-10 https://docs.python.org/3/library/shelve.html +RedirectTemp /3-11 https://docs.python.org/3/library/dbm.html +RedirectTemp /3-12 https://docs.python.org/3/library/pickle.html +RedirectTemp /3-13 https://nedbatchelder.com/blog/202006/pickles_nine_flaws.html +RedirectTemp /3-14 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py#L813 +RedirectTemp /3-15 https://mail.python.org/pipermail/python-dev/2015-May/140003.html +RedirectTemp /3-16 https://bugs.python.org/issue18986 +RedirectTemp /3-17 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/transformdict.py +RedirectTemp /3-18 http://gandenberger.org/2018/03/10/ordered-dicts-vs-ordereddict/ +RedirectTemp /3-19 https://www.pypy.org/ +RedirectTemp /3-20 https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html +RedirectTemp /3-21 https://www.npopov.com/2014/12/22/PHPs-new-hashtable-implementation.html +RedirectTemp /3-22 https://www.youtube.com/watch?v=66P5FMkWoVU +RedirectTemp /3-23 https://pyvideo.org/video/276/the-mighty-dictionary-55/ +RedirectTemp /3-24 https://www.youtube.com/watch?v=p33CVV29OG8 +RedirectTemp /3-25 https://docs.python.org/3/whatsnew/3.6.html#new-dict-implementation +RedirectTemp /3-26 https://github.com/python/cpython/blob/cf7eaa4617295747ee5646c4e2b7e7a16d7c64ab/Objects/dictobject.c +RedirectTemp /3-27 https://github.com/python/cpython/blob/cf7eaa4617295747ee5646c4e2b7e7a16d7c64ab/Objects/dictnotes.txt +RedirectTemp /3-28 https://www.youtube.com/watch?v=tGAngdU_8D8 +RedirectTemp /3-29 https://speakerdeck.com/ramalho/python-set-practice-at-pycon +RedirectTemp /3-30 https://github.com/standupdev/uintset +RedirectTemp /3-31 https://spectrum.ieee.org/hans-peter-luhn-and-the-birth-of-the-hashing-algorithm +RedirectTemp /3-32 http://www.json.org/fatfree.html +RedirectTemp /3-33 https://twitter.com/mitsuhiko/status/1229385843585974272 +############################################################ 04 +RedirectTemp /4-1 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 +RedirectTemp /4-2 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ +RedirectTemp /4-3 https://www.fluentpython.com/extra/parsing-binary-struct/ +RedirectTemp /4-4 https://www.fluentpython.com/extra/multi-character-emojis/ +RedirectTemp /4-5 https://w3techs.com/technologies/overview/character_encoding +RedirectTemp /4-6 https://docs.python.org/3/library/codecs.html#codecs.register_error +RedirectTemp /4-7 https://docs.python.org/3/library/stdtypes.html#str.isascii +RedirectTemp /4-8 https://pypi.org/project/chardet/ +RedirectTemp /4-9 https://docs.python.org/3/library/codecs.html#encodings-and-unicode +RedirectTemp /4-10 https://nedbatchelder.com/text/unipain/unipain.html +RedirectTemp /4-11 https://devblogs.microsoft.com/commandline/windows-command-line-unicode-and-utf-8-output-text-buffer/ +RedirectTemp /4-12 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONIOENCODING +RedirectTemp /4-13 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONLEGACYWINDOWSSTDIO +RedirectTemp /4-14 https://docs.python.org/3/library/locale.html#locale.getpreferredencoding +RedirectTemp /4-15 http://www.w3.org/TR/charmod-norm/ +RedirectTemp /4-16 https://docs.python.org/3/library/locale.html?highlight=strxfrm#locale.strxfrm +RedirectTemp /4-17 https://pypi.org/project/pyuca/ +RedirectTemp /4-18 https://github.com/jtauber/pyuca +RedirectTemp /4-19 http://www.unicode.org/Public/UCA/6.3.0/allkeys.txt +RedirectTemp /4-20 https://pypi.org/project/PyICU/ +RedirectTemp /4-21 https://docs.python.org/3.10/library/stdtypes.html#str.isalpha +RedirectTemp /4-22 https://en.wikipedia.org/wiki/Unicode_character_property#General_Category +RedirectTemp /4-23 https://en.wikipedia.org/wiki/Unicode_character_property +RedirectTemp /4-24 https://github.com/microsoft/terminal +RedirectTemp /4-25 https://docs.python.org/3/library/unicodedata.html +RedirectTemp /4-26 https://docs.python.org/3/reference/lexical_analysis.html#string-literal-concatenation +RedirectTemp /4-27 https://docs.python.org/3/library/re.html +RedirectTemp /4-28 https://nedbatchelder.com/text/unipain.html +RedirectTemp /4-29 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 +RedirectTemp /4-30 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ +RedirectTemp /4-31 https://regebro.wordpress.com/2011/03/23/unconfusing-unicode-what-is-unicode/ +RedirectTemp /4-32 https://docs.python.org/3/howto/unicode.html +RedirectTemp /4-33 https://diveintopython3.net/strings.html +RedirectTemp /4-34 https://diveintopython3.net/ +RedirectTemp /4-35 https://finderiko.com/python-book +RedirectTemp /4-36 https://docs.python.org/3.0/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit +RedirectTemp /4-37 https://lucumr.pocoo.org/2013/7/2/the-updated-guide-to-unicode/ +RedirectTemp /4-38 http://python-notes.curiousefficiency.org/en/latest/python3/binary_protocols.html +RedirectTemp /4-39 http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html +RedirectTemp /4-40 https://docs.python.org/3/library/codecs.html#standard-encodings +RedirectTemp /4-41 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Tools/unicode/listcodecs.py +RedirectTemp /4-42 https://www.oreilly.com/library/view/unicode-explained/059610121X/ +RedirectTemp /4-43 https://www.informit.com/store/unicode-demystified-a-practical-programmers-guide-to-9780201700527 +RedirectTemp /4-44 https://unicodebook.readthedocs.io/index.html +RedirectTemp /4-45 https://www.w3.org/International/wiki/Case_folding +RedirectTemp /4-46 http://www.w3.org/TR/charmod-norm/ +RedirectTemp /4-47 http://unicode.org/reports/tr15/ +RedirectTemp /4-48 http://www.unicode.org/faq/normalization.html +RedirectTemp /4-49 http://www.unicode.org/ +RedirectTemp /4-50 http://www.macchiato.com/unicode/nfc-faq +RedirectTemp /4-51 https://stories.moma.org/the-original-emoji-set-has-been-added-to-the-museum-of-modern-arts-collection-c6060e141f61?gi=c403f5a840a5 +RedirectTemp /4-52 https://emojipedia.org/ +RedirectTemp /4-53 https://blog.emojipedia.org/correcting-the-record-on-the-first-emoji-set/ +RedirectTemp /4-54 http://emojitracker.com/ +RedirectTemp /4-55 http://www.unicode.org/glossary/#plain_text +RedirectTemp /4-56 http://www.methods.co.nz/asciidoc/ +RedirectTemp /4-57 https://atlas.oreilly.com/ +############################################################ 05 +RedirectTemp /5-1 https://docs.python.org/3/library/typing.html#typing.TypedDict +RedirectTemp /5-2 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations +RedirectTemp /5-3 https://docs.python.org/3/library/typing.html#typing.get_type_hints +RedirectTemp /5-4 https://docs.python.org/3.8/library/collections.html#collections.somenamedtuple._asdict +RedirectTemp /5-5 https://www.jetbrains.com/pycharm/ +RedirectTemp /5-6 https://www.python.org/dev/peps/pep-0484/#acceptable-type-hints +RedirectTemp /5-7 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass +RedirectTemp /5-8 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass +RedirectTemp /5-9 https://docs.python.org/3/library/dataclasses.html +RedirectTemp /5-10 https://docs.python.org/3/library/dataclasses.html#inheritance +RedirectTemp /5-11 https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations +RedirectTemp /5-12 https://dublincore.org/specifications/dublin-core/ +RedirectTemp /5-13 https://en.wikipedia.org/wiki/Dublin_Core +RedirectTemp /5-14 https://martinfowler.com/bliki/CodeSmell.html +RedirectTemp /5-15 https://martinfowler.com/books/refactoring.html +RedirectTemp /5-16 https://www.python.org/dev/peps/pep-0634/#class-patterns +RedirectTemp /5-17 https://docs.python.org/3/library/dataclasses.html +RedirectTemp /5-18 https://www.python.org/dev/peps/pep-0557/#id47 +RedirectTemp /5-19 https://www.python.org/dev/peps/pep-0557/#id48 +RedirectTemp /5-20 https://www.python.org/dev/peps/pep-0557/#id33 +RedirectTemp /5-21 https://realpython.com +RedirectTemp /5-22 https://realpython.com/python-data-classes/ +RedirectTemp /5-23 https://www.youtube.com/watch?v=T-TwcmT6Rcw +RedirectTemp /5-24 https://www.attrs.org/en/stable/ +RedirectTemp /5-25 https://glyph.twistedmatrix.com/2016/08/attrs.html +RedirectTemp /5-26 https://www.attrs.org/en/stable/why.html +RedirectTemp /5-27 https://github.com/dabeaz/cluegen +RedirectTemp /5-28 https://refactoring.guru/ +RedirectTemp /5-29 https://refactoring.guru/smells/data-class +RedirectTemp /5-30 https://web.archive.org/web/20190204130328/http://catb.org/esr/jargon/html/G/Guido.html +RedirectTemp /5-31 https://web.archive.org/web/20190211161610/http://catb.org/esr/jargon/html/index.html +RedirectTemp /5-32 https://www.attrs.org/en/stable/ +############################################################ 06 +RedirectTemp /6-1 https://www.olin.edu/faculty/profile/lynn-andrea-stein/ +RedirectTemp /6-2 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types +RedirectTemp /6-3 https://pythontutor.com/ +RedirectTemp /6-4 https://docs.python.org/3/library/copy.html +RedirectTemp /6-5 https://en.wikipedia.org/wiki/Principle_of_least_astonishment +RedirectTemp /6-6 https://docs.python.org/3/reference/datamodel.html#object.%5C_%5C_del__ +RedirectTemp /6-7 https://emptysqua.re/blog/pypy-garbage-collection-and-a-deadlock/ +RedirectTemp /6-8 https://www.youtube.com/watch?v=HHFCFJSPWrI&feature=youtu.be +RedirectTemp /6-9 http://pymotw.com/3/copy/ +RedirectTemp /6-10 http://pymotw.com/3/weakref/ +RedirectTemp /6-11 https://docs.python.org/3/library/gc.html +RedirectTemp /6-12 https://devguide.python.org/garbage_collector/ +RedirectTemp /6-13 https://devguide.python.org/ +RedirectTemp /6-14 https://www.python.org/dev/peps/pep-0442/ +RedirectTemp /6-15 https://en.wikipedia.org/wiki/String_interning +RedirectTemp /6-16 https://en.wikipedia.org/wiki/Haddocks%27_Eyes +RedirectTemp /6-17 https://thp.io/2012/python-gc/python_gc_final_2012-01-22.pdf +############################################################ 07 +RedirectTemp /7-1 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html +RedirectTemp /7-2 https://www.fluentpython.com/extra/function-introspection/ +RedirectTemp /7-3 https://docs.python.org/3/library/functions.html#map +RedirectTemp /7-4 https://en.wikipedia.org/wiki/Functional_programming +RedirectTemp /7-5 https://docs.python.org/3/howto/functional.html +RedirectTemp /7-6 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy +RedirectTemp /7-7 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters +RedirectTemp /7-8 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters +RedirectTemp /7-9 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/functools.py#L341 +RedirectTemp /7-10 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy +RedirectTemp /7-11 https://docs.python.org/3/howto/functional.html +RedirectTemp /7-12 https://stackoverflow.com/questions/3252228/python-why-is-functools-partial-necessary +RedirectTemp /7-13 https://speakerdeck.com/ramalho/beyond-paradigms-berlin-edition +RedirectTemp /7-14 https://www.youtube.com/watch?v=bF3a2VYXxa0 +RedirectTemp /7-15 http://cs.brown.edu/~sk/Publications/Papers/Published/sk-teach-pl-post-linnaean/ +RedirectTemp /7-16 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html +RedirectTemp /7-17 https://raw.githubusercontent.com/python/cpython/main/Misc/HISTORY +RedirectTemp /7-18 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html +############################################################ 08 +RedirectTemp /8-1 https://www.python.org/dev/peps/pep-0484/#non-goals +RedirectTemp /8-2 https://github.com/python/typing/issues/182 +RedirectTemp /8-3 https://github.com/python/mypy/issues/731 +RedirectTemp /8-4 https://github.com/google/pytype +RedirectTemp /8-5 https://github.com/Microsoft/pyright +RedirectTemp /8-6 https://pyre-check.org/ +RedirectTemp /8-7 https://mypy.readthedocs.io/en/stable/introduction.html +RedirectTemp /8-8 https://mypy.readthedocs.io/en/stable/config_file.html +RedirectTemp /8-9 https://pypi.org/project/flake8/ +RedirectTemp /8-10 https://pypi.org/project/blue/ +RedirectTemp /8-11 https://pypi.org/project/black/ +RedirectTemp /8-12 https://wefearchange.org/2020/11/steeringcouncil.rst.html +RedirectTemp /8-13 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes +RedirectTemp /8-14 https://en.wikipedia.org/wiki/Barbara_Liskov +RedirectTemp /8-15 https://en.wikipedia.org/wiki/Behavioral_subtyping +RedirectTemp /8-16 https://www.python.org/dev/peps/pep-0585/#implementation +RedirectTemp /8-17 https://docs.python.org/3/library/typing.html#module-contents +RedirectTemp /8-18 https://en.wikipedia.org/wiki/Geohash +RedirectTemp /8-19 https://en.wikipedia.org/wiki/Inverted_index +RedirectTemp /8-20 https://docs.python.org/3/library/typing.html#typing.List +RedirectTemp /8-21 https://docs.python.org/3/library/typing.html#typing.Dict +RedirectTemp /8-22 https://docs.python.org/3/library/typing.html#typing.Set +RedirectTemp /8-23 https://www.python.org/dev/peps/pep-0585/#implementation +RedirectTemp /8-24 https://docs.python.org/3/library/numbers.html +RedirectTemp /8-25 https://docs.python.org/3/library/typing.html#typing.List +RedirectTemp /8-26 https://github.com/python/typeshed +RedirectTemp /8-27 https://github.com/python/typeshed/blob/66cd36268a6a667714efaa27198a41d0d7f89477/stdlib/2and3/math.pyi#L45 +RedirectTemp /8-28 https://docs.python.org/3/library/statistics.html#statistics.mode +RedirectTemp /8-29 https://github.com/python/cpython/blob/822efa5695b5ba6c2316c1400e4e9ec2546f7ea5/Lib/statistics.py#L534 +RedirectTemp /8-30 https://github.com/python/typeshed/blob/e1e99245bb46223928eba68d4fc74962240ba5b4/stdlib/3/statistics.pyi +RedirectTemp /8-31 https://docs.python.org/3/library/statistics.html#statistics.mode +RedirectTemp /8-32 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/3/statistics.pyi#L32 +RedirectTemp /8-33 https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ +RedirectTemp /8-34 https://docs.python.org/3/library/typing.html#typing.Callable +RedirectTemp /8-35 https://pypi.org/project/blue/ +RedirectTemp /8-36 https://www.python.org/dev/peps/pep-0484/#id38 +RedirectTemp /8-37 https://docs.google.com/document/d/1aXs1tpwzPjW9MdsG5dI7clNFyYayFBkcXwRDo-qvbIk/preview +RedirectTemp /8-38 https://www.oreilly.com/library/view/the-best-software/9781590595008/ +RedirectTemp /8-39 https://www.youtube.com/watch?v=YFexUDjHO6w +RedirectTemp /8-40 https://www.youtube.com/watch?v=YFexUDjHO6w&t=13m40s +RedirectTemp /8-41 https://bernat.tech/posts/the-state-of-type-hints-in-python/ +RedirectTemp /8-42 https://realpython.com/python-type-checking/ +RedirectTemp /8-43 https://cjolowicz.github.io/posts/hypermodern-python-04-typing/ +RedirectTemp /8-44 https://mypy.readthedocs.io/en/stable/index.html +RedirectTemp /8-45 https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html +RedirectTemp /8-46 https://mypy.readthedocs.io/en/stable/common_issues.html +RedirectTemp /8-47 https://github.com/typeddjango/awesome-python-typing +RedirectTemp /8-48 https://docs.python.org/3/library/functions.html#max +RedirectTemp /8-49 https://en.wikipedia.org/wiki/Linguistic_relativity +RedirectTemp /8-50 https://pypistats.org/top +RedirectTemp /8-51 https://github.com/psf/requests/issues/3855 +RedirectTemp /8-52 https://lwn.net/Articles/643399/ +RedirectTemp /8-53 https://docs.python-requests.org/en/master/api/#requests.request +RedirectTemp /8-54 https://queue.acm.org/detail.cfm?id=1039523 +############################################################ 09 +RedirectTemp /9-1 https://docs.python.org/3/library/dis.html +RedirectTemp /9-2 https://en.wikipedia.org/wiki/Memoization +RedirectTemp /9-3 https://numpy.org/doc/stable/user/basics.types.html +RedirectTemp /9-4 https://docs.python.org/3/library/functools.html#functools.singledispatch +RedirectTemp /9-5 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/README.md +RedirectTemp /9-6 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/01-how-you-implemented-your-python-decorator-is-wrong.md +RedirectTemp /9-7 https://wrapt.readthedocs.io/en/latest/ +RedirectTemp /9-8 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch09.html +RedirectTemp /9-9 https://pypi.org/project/decorator/ +RedirectTemp /9-10 https://wiki.python.org/moin/PythonDecoratorLibrary +RedirectTemp /9-11 http://web.archive.org/web/20201109032203/http://effbot.org/zone/closure.htm +RedirectTemp /9-12 https://www.python.org/dev/peps/pep-3104/ +RedirectTemp /9-13 https://www.python.org/dev/peps/pep-0227/ +RedirectTemp /9-14 https://www.python.org/dev/peps/pep-0443/ +RedirectTemp /9-15 https://www.artima.com/weblogs/viewpost.jsp?thread=101605 +RedirectTemp /9-16 https://reg.readthedocs.io/en/latest/ +RedirectTemp /9-17 https://morepath.readthedocs.io/en/latest/ +RedirectTemp /9-18 https://www.gnu.org/software/emacs/manual/html_node/elisp/Dynamic-Binding.html +RedirectTemp /9-19 http://www.paulgraham.com/rootsoflisp.html +RedirectTemp /9-20 http://www-formal.stanford.edu/jmc/recursive/recursive.html +RedirectTemp /9-21 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this +############################################################ 10 +RedirectTemp /10-1 https://en.wikipedia.org/wiki/Software_design_pattern +RedirectTemp /10-2 https://en.wikipedia.org/wiki/Iterator_pattern +RedirectTemp /10-3 https://github.com/python/mypy/issues/9397 +RedirectTemp /10-4 http://www.norvig.com/design-patterns/index.htm +RedirectTemp /10-5 https://pyvideo.org/video/1110/python-design-patterns/ +RedirectTemp /10-6 http://www.aleax.it/gdd_pydp.pdf +RedirectTemp /10-7 https://perl.plover.com/yak/design/ +RedirectTemp /10-8 https://en.wikipedia.org/wiki/Turtles_all_the_way_down +############################################################ 11 +RedirectTemp /11-1 https://blog.startifact.com/posts/older/what-is-pythonic.html +RedirectTemp /11-2 https://julien.danjou.info/guide-python-static-class-abstract-methods/ +RedirectTemp /11-3 https://docs.python.org/3/library/string.html#formatspec +RedirectTemp /11-4 https://docs.python.org/3/reference/lexical_analysis.html#f-strings +RedirectTemp /11-5 https://docs.python.org/3/library/string.html#format-string-syntax +RedirectTemp /11-6 https://docs.python.org/3/library/string.html#formatspec +RedirectTemp /11-7 https://docs.python.org/3/reference/datamodel.html#object.__hash__ +RedirectTemp /11-8 https://web.archive.org/web/20161025185040/http://pythonpaste.org/StyleGuide.html +RedirectTemp /11-9 https://docs.python.org/3/tutorial/modules.html#more-on-modules +RedirectTemp /11-10 https://docs.python.org/3/library/gettext.html#gettext.NullTranslations +RedirectTemp /11-11 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/mem_test.py +RedirectTemp /11-12 https://docs.python.org/3/reference/datamodel.html#basic-customization +RedirectTemp /11-13 http://esug.org/data/HistoricalDocuments/TheSmalltalkReport/ST07/04wo.pdf +RedirectTemp /11-14 https://www.artima.com/articles/the-simplest-thing-that-could-possibly-work#part3 +RedirectTemp /11-15 https://docs.oracle.com/javase/tutorial/essential/environment/security.html +RedirectTemp /11-16 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/private/Expose.java +RedirectTemp /11-17 https://docs.oracle.com/javase/tutorial/essential/environment/security.html +############################################################ 12 +RedirectTemp /12-1 https://en.wikipedia.org/wiki/Vector_space_model +RedirectTemp /12-2 https://pypi.org/project/gensim/ +RedirectTemp /12-3 https://docs.python.org/3/library/functions.html#enumerate +RedirectTemp /12-4 https://mathworld.wolfram.com/Hypersphere.html +RedirectTemp /12-5 https://en.wikipedia.org/wiki/Fold_(higher-order_function) +RedirectTemp /12-6 https://docs.python.org/2.5/whatsnew/pep-357.html +RedirectTemp /12-7 https://docs.python.org/3/reference/datamodel.html#special-method-names +RedirectTemp /12-8 https://en.wikipedia.org/wiki/KISS_principle +RedirectTemp /12-9 https://mail.python.org/pipermail/python-list/2000-July/046184.html +RedirectTemp /12-10 https://en.wikipedia.org/wiki/Duck_typing +RedirectTemp /12-11 https://mail.python.org/mailman/listinfo/python-list +RedirectTemp /12-12 https://mail.python.org/pipermail/python-list/2003-April/218568.html +############################################################ 13 +RedirectTemp /13-1 https://docs.python.org/3/c-api/index.html +RedirectTemp /13-2 https://docs.python.org/3/c-api/sequence.html +RedirectTemp /13-3 https://github.com/python/cpython/blob/31ceccb2c77854893f3a754aca04bedd74bedb10/Lib/_collections_abc.py#L870 +RedirectTemp /13-4 https://en.wikipedia.org/wiki/Monkey_patch +RedirectTemp /13-5 https://www.gevent.org/api/gevent.monkey.html +RedirectTemp /13-6 https://docs.python.org/3/library/random.html#random.shuffle +RedirectTemp /13-7 https://docs.python.org/3/reference/datamodel.html#emulating-container-types +RedirectTemp /13-8 https://docs.python.org/3/library/collections.html#collections.namedtuple +RedirectTemp /13-9 https://github.com/python/typeshed/blob/24afb531ffd07083d6a74be917342195062f7277/stdlib/collections/__init__.pyi +RedirectTemp /13-10 https://docs.python.org/3/glossary.html#term-abstract-base-class +RedirectTemp /13-11 https://en.wikipedia.org/wiki/Duck_typing#History +RedirectTemp /13-12 http://ptgmedia.pearsoncmg.com/images/020163371x/items/item33.html +RedirectTemp /13-13 https://docs.python.org/3/library/bisect.html#bisect.bisect +RedirectTemp /13-14 https://github.com/python/cpython/blob/main/Lib/_collections_abc.py +RedirectTemp /13-15 https://github.com/python/cpython/blob/main/Lib/abc.py +RedirectTemp /13-16 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes +RedirectTemp /13-17 https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable +RedirectTemp /13-18 https://docs.python.org/3/library/abc.html +RedirectTemp /13-19 https://docs.python.org/dev/library/abc.html#abc.abstractmethod +RedirectTemp /13-20 https://docs.python.org/dev/library/abc.html +RedirectTemp /13-21 https://docs.python.org/3/library/os.html#os.urandom +RedirectTemp /13-22 https://github.com/python/mypy/issues/2922 +RedirectTemp /13-23 https://docs.python.org/3/library/stdtypes.html#truth +RedirectTemp /13-24 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py +RedirectTemp /13-25 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L369 +RedirectTemp /13-26 https://github.com/python/cpython/blob/c0a9afe2ac1820409e6173bd1893ebee2cf50270/Lib/abc.py#L196 +RedirectTemp /13-27 https://bugs.python.org/issue31333 +RedirectTemp /13-28 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Modules/_abc.c#L605 +RedirectTemp /13-29 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L881 +RedirectTemp /13-30 https://docs.python.org/3/library/typing.html#protocols +RedirectTemp /13-31 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Lib/typing.py#L1751 +RedirectTemp /13-32 https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ +RedirectTemp /13-33 https://martinfowler.com/bliki/RoleInterface.html +RedirectTemp /13-34 https://en.wikipedia.org/wiki/Interface_segregation_principle +RedirectTemp /13-35 https://github.com/python/typeshed/blob/master/CONTRIBUTING.md +RedirectTemp /13-36 https://gist.github.com/asukakenji/ac8a05644a2e98f1d5ea8c299541fce9 +RedirectTemp /13-37 https://www.python.org/dev/peps/pep-0544/#runtime-checkable-decorator-and-narrowing-types-by-isinstance +RedirectTemp /13-38 https://www.python.org/dev/peps/pep-0544/#merging-and-extending-protocols +RedirectTemp /13-39 https://numpy.org/devdocs/user/basics.types.html +RedirectTemp /13-40 https://github.com/python/typeshed/blob/master/stdlib/statistics.pyi +RedirectTemp /13-41 https://bugs.python.org/issue41974 +RedirectTemp /13-42 https://glyph.twistedmatrix.com/2020/07/new-duck.html +RedirectTemp /13-43 https://glyph.twistedmatrix.com/2021/03/interfaces-and-protocols.html +RedirectTemp /13-44 https://plone.org/ +RedirectTemp /13-45 https://trypyramid.com/ +RedirectTemp /13-46 https://twistedmatrix.com/trac/ +RedirectTemp /13-47 https://www.artima.com/articles/contracts-in-python +RedirectTemp /13-48 https://martinfowler.com/bliki/DynamicTyping.html +RedirectTemp /13-49 https://martinfowler.com/bliki/RoleInterface.html +RedirectTemp /13-50 https://mypy.readthedocs.io/en/stable/protocols.html +RedirectTemp /13-51 https://pymotw.com/3/abc/index.html +RedirectTemp /13-52 https://www.python.org/dev/peps/pep-3119/ +RedirectTemp /13-53 https://www.python.org/dev/peps/pep-3141/ +RedirectTemp /13-54 https://docs.python.org/3/library/numbers.html +RedirectTemp /13-55 https://github.com/python/mypy/issues/3186 +RedirectTemp /13-56 https://stackoverflow.com/questions/69334475/how-to-hint-at-number-types-i-e-subclasses-of-number-not-numbers-themselv/69383462#69383462 +RedirectTemp /13-57 https://github.com/python/mypy/issues/3186 +RedirectTemp /13-58 https://martinfowler.com/articles/lean-inception/ +RedirectTemp /13-59 https://martinfowler.com +RedirectTemp /13-60 https://www.jetbrains.com/pycharm/ +RedirectTemp /13-61 https://wingware.com/ +RedirectTemp /13-62 https://code.visualstudio.com/ +############################################################ 14 +RedirectTemp /14-1 http://worrydream.com/EarlyHistoryOfSmalltalk/ +RedirectTemp /14-2 https://docs.python.org/3/tutorial/classes.html +RedirectTemp /14-3 https://docs.python.org/3/library/collections.html#ordereddict-examples-and-recipes +RedirectTemp /14-4 https://discuss.python.org/t/is-it-time-to-deprecate-unbound-super-methods/1833 +RedirectTemp /14-5 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /14-6 https://docs.python.org/3/library/collections.html +RedirectTemp /14-7 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/strkeydict_dictsub.py +RedirectTemp /14-8 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /14-9 https://en.wikipedia.org/wiki/Breadth-first_search +RedirectTemp /14-10 https://www.python.org/download/releases/2.3/mro/ +RedirectTemp /14-11 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/uppermixin.py +RedirectTemp /14-12 https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html +RedirectTemp /14-13 https://docs.python.org/3/library/collections.abc.html +RedirectTemp /14-14 https://github.com/python/cpython/blob/8ece98a7e418c3c68a4c61bc47a2d0931b59a889/Lib/collections/__init__.py#L1084 +RedirectTemp /14-15 https://docs.python.org/3/library/http.server.html +RedirectTemp /14-16 https://github.com/python/cpython/blob/17c23167942498296f0bdfffe52e72d53d66d693/Lib/http/server.py#L144 +RedirectTemp /14-17 https://github.com/python/cpython/blob/699ee016af5736ffc80f68359617611a22b72943/Lib/socketserver.py#L664 +RedirectTemp /14-18 https://docs.python.org/3/library/socketserver.html#socketserver.ForkingMixIn +RedirectTemp /14-19 https://docs.python.org/3/library/os.html#os.fork +RedirectTemp /14-20 https://en.wikipedia.org/wiki/POSIX +RedirectTemp /14-21 http://ccbv.co.uk/ +RedirectTemp /14-22 https://github.com/django/django/tree/main/django/views/generic +RedirectTemp /14-23 https://en.wikipedia.org/wiki/Template_method_pattern +RedirectTemp /14-24 https://docs.python.org/3/library/tkinter.html +RedirectTemp /14-25 https://docs.python.org/3/library/tkinter.ttk.html +RedirectTemp /14-26 https://docs.oracle.com/javase/10/docs/api/java/awt/package-tree.html +RedirectTemp /14-27 https://docs.oracle.com/javase/10/docs/api/javax/swing/package-tree.html +RedirectTemp /14-28 https://squeak.org/ +RedirectTemp /14-29 https://github.com/django/django/blob/b64db05b9cedd96905d637a2d824cbbf428e40e7/django/views/generic/list.py#L194 +RedirectTemp /14-30 https://github.com/python/cpython/blob/8ed183391241f0c73e7ba7f42b1d49fc02985f7b/Lib/tkinter/__init__.py#L2618 +RedirectTemp /14-31 https://docs.python.org/3/library/socketserver.html +RedirectTemp /14-32 https://docs.python.org/3/library/socketserver.html#socketserver.BaseServer +RedirectTemp /14-33 https://github.com/python/cpython/blob/699ee016af5736ffc80f68359617611a22b72943/Lib/socketserver.py#L153 +RedirectTemp /14-34 https://docs.python.org/3/library/typing.html#typing.final +RedirectTemp /14-35 https://docs.python.org/3/library/typing.html#typing.Final +RedirectTemp /14-36 https://docs.python.org/3/library/collections.abc.html +RedirectTemp /14-37 https://hynek.me/articles/python-subclassing-redux/ +RedirectTemp /14-38 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch08.html#super +RedirectTemp /14-39 https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ +RedirectTemp /14-40 https://fuhm.net/super-harmful/ +RedirectTemp /14-41 https://stackoverflow.com/questions/30190185/how-to-use-super-with-one-argument/30190341#30190341 +RedirectTemp /14-42 https://www.artima.com/weblogs/viewpost.jsp?thread=246488 +RedirectTemp /14-43 https://www.artima.com/weblogs/viewpost.jsp?thread=281127 +RedirectTemp /14-44 https://www.artima.com/weblogs/viewpost.jsp?thread=246341 +RedirectTemp /14-45 https://www.artima.com/weblogs/viewpost.jsp?thread=246483 +RedirectTemp /14-46 https://www.artima.com/weblogs/viewpost.jsp?thread=236275 +RedirectTemp /14-47 https://www.artima.com/weblogs/viewpost.jsp?thread=236278 +RedirectTemp /14-48 https://www.artima.com/weblogs/viewpost.jsp?thread=237121 +RedirectTemp /14-49 https://python-patterns.guide/gang-of-four/composition-over-inheritance/ +RedirectTemp /14-50 https://python-patterns.guide/ +RedirectTemp /14-51 https://www.youtube.com/watch?v=3MNVP9-hglc +RedirectTemp /14-52 http://worrydream.com/EarlyHistoryOfSmalltalk/ +RedirectTemp /14-53 https://en.wikipedia.org/wiki/Polymorphism_(computer_science) +############################################################ 15 +RedirectTemp /15-1 https://www.youtube.com/watch?v=csL8DLXGNlU&t=92m5s +RedirectTemp /15-2 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/2and3/builtins.pyi#L1434 +RedirectTemp /15-3 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/2and3/builtins.pyi +RedirectTemp /15-4 https://twitter.com/gwidion/status/1265384692464967680 +RedirectTemp /15-5 https://pypi.org/project/pydantic/ +RedirectTemp /15-6 https://google.github.io/pytype/faq.html +RedirectTemp /15-7 https://google.github.io/pytype/faq.html +RedirectTemp /15-8 https://lxml.de/ +RedirectTemp /15-9 https://docs.python.org/3/library/xml.etree.elementtree.html +RedirectTemp /15-10 https://mypy.readthedocs.io/en/stable/common_issues.html +RedirectTemp /15-11 https://mypy.readthedocs.io/en/stable/common_issues.html#types-of-empty-collections +RedirectTemp /15-12 https://github.com/python/typing/issues/182 +RedirectTemp /15-13 https://pypi.org/project/pydantic/ +RedirectTemp /15-14 https://mypy.readthedocs.io/en/stable/type_narrowing.html#casts +RedirectTemp /15-15 https://github.com/python/cpython/blob/bee66d3cb98e740f9d8057eb7f503122052ca5d8/Lib/typing.py#L1340 +RedirectTemp /15-16 https://www.python.org/dev/peps/pep-0484/#casts +RedirectTemp /15-17 https://github.com/python/typeshed/issues/5535 +RedirectTemp /15-18 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams +RedirectTemp /15-19 https://github.com/python/cpython/blob/b798ab06937f8bb24b444a49dd42e11fff15e654/Lib/test/test_asyncio/test_server.py#L55 +RedirectTemp /15-20 https://en.wikipedia.org/wiki/Code_smell +RedirectTemp /15-21 https://mail.python.org/archives/list/typing-sig@python.org/message/5LCWMN2UY2UQNLC5Z47GHBZKSPZW4I63/ +RedirectTemp /15-22 https://mypy.readthedocs.io/en/stable/error_codes.html#error-codes +RedirectTemp /15-23 https://github.com/fluentpython/example-code-2e/blob/master/15-more-types/clip_annot.py +RedirectTemp /15-24 https://docs.python.org/3/library/typing.html#introspection-helpers +RedirectTemp /15-25 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations +RedirectTemp /15-26 https://www.python.org/dev/peps/pep-0563/#abstract +RedirectTemp /15-27 https://mail.python.org/archives/list/python-dev@python.org/message/ZBJ7MD6CSGM6LZAOTET7GXAVBZB7O77O/ +RedirectTemp /15-28 https://docs.python.org/3.10/howto/annotations.html +RedirectTemp /15-29 https://docs.python.org/3/library/typing.html#user-defined-generic-types +RedirectTemp /15-30 https://github.com/python/typeshed/blob/bfc83c365a0b26ab16586beac77ff16729d0e473/stdlib/builtins.pyi#L743 +RedirectTemp /15-31 https://docs.python.org/3.10/library/typing.html#typing.FrozenSet +RedirectTemp /15-32 https://docs.python.org/3.10/library/typing.html#typing.Generator +RedirectTemp /15-33 https://docs.python.org/3.10/library/typing.html#typing.AsyncGenerator +RedirectTemp /15-34 https://github.com/python/cpython/blob/46b16d0bdbb1722daed10389e27226a2370f1635/Lib/typing.py#L1786 +RedirectTemp /15-35 https://github.com/python/typeshed/blob/2a9f081abbf01134e4e04ced6a750107db904d70/stdlib/builtins.pyi#L239 +RedirectTemp /15-36 https://www.oreilly.com/library/view/robust-python/9781098100650/ +RedirectTemp /15-37 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance +RedirectTemp /15-38 https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generic-types +RedirectTemp /15-39 https://mypy.readthedocs.io/en/stable/common_issues.html#variance +RedirectTemp /15-40 https://www.artima.com/weblogs/viewpost.jsp?thread=85551 +RedirectTemp /15-41 https://dl.acm.org/action/cookieAbsent +RedirectTemp /15-42 http://bracha.org/pluggableTypesPosition.pdf +RedirectTemp /15-43 https://www.researchgate.net/publication/213886116_Static_Typing_Where_Possible_Dynamic_Typing_When_Needed_The_End_of_the_Cold_War_Between_Programming_Languages +RedirectTemp /15-44 https://www.atomickotlin.com/atomickotlin/ +RedirectTemp /15-45 https://www.informit.com/store/effective-java-9780134685991 +RedirectTemp /15-46 https://www.manning.com/books/programming-with-types +RedirectTemp /15-47 https://www.oreilly.com/library/view/programming-typescript/9781492037644/ +RedirectTemp /15-48 https://www.informit.com/store/dart-programming-language-9780321927705 +RedirectTemp /15-49 https://www.yodaiken.com/2017/09/15/bad-ideas-in-type-theory/ +RedirectTemp /15-50 https://www.yodaiken.com/2017/11/30/types-considered-harmful-ii/ +RedirectTemp /15-51 https://web.archive.org/web/20071010002142/http://weblogs.java.net/blog/arnold/archive/2005/06/generics_consid_1.html +RedirectTemp /15-52 https://github.com/python/cpython/blob/3e7ee02327db13e4337374597cdc4458ecb9e3ad/Lib/asyncio/trsock.py#L5 +RedirectTemp /15-53 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance +############################################################ 16 +RedirectTemp /16-1 http://www.gotw.ca/publications/c_family_interview.htm +RedirectTemp /16-2 https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations +RedirectTemp /16-3 https://docs.python.org/3/reference/datamodel.html#object.__neg__ +RedirectTemp /16-4 https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#boolean-indexing +RedirectTemp /16-5 https://docs.python.org/3/library/collections.html#collections.Counter +RedirectTemp /16-6 https://docs.python.org/3/reference/datamodel.html#emulating-container-types +RedirectTemp /16-7 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations +RedirectTemp /16-8 https://www.fluentpython.com/lingo/#fail-fast +RedirectTemp /16-9 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Objects/typeobject.c#L4598 +RedirectTemp /16-10 https://neopythonic.blogspot.com/2019/03/why-operators-are-useful.html +RedirectTemp /16-11 https://treyhunner.com/2019/03/python-deep-comparisons-and-code-readability/ +RedirectTemp /16-12 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations +RedirectTemp /16-13 https://docs.python.org/3/library/pathlib.html +RedirectTemp /16-14 https://pypi.org/project/scapy/ +RedirectTemp /16-15 https://scapy.readthedocs.io/en/latest/usage.html#stacking-layers +RedirectTemp /16-16 https://docs.python.org/3/library/functools.html#functools.total_ordering +RedirectTemp /16-17 https://wiki.illinois.edu//wiki/download/attachments/273416327/ingalls.pdf +RedirectTemp /16-18 https://wiki.illinois.edu//wiki/download/attachments/273416327/double-dispatch.pdf +RedirectTemp /16-19 http://www.gotw.ca/publications/c_family_interview.htm +RedirectTemp /16-20 http://www.gotw.ca/publications/c_family_interview.htm +RedirectTemp /16-21 https://doc.rust-lang.org/std/ops/index.html +RedirectTemp /16-22 https://www.fluentpython.com/lingo/#lazy +############################################################ 17 +RedirectTemp /17-1 http://www.paulgraham.com/icad.html +RedirectTemp /17-2 https://en.wikipedia.org/wiki/Sentinel_value +RedirectTemp /17-3 https://docs.python.org/3.10/library/functions.html#iter +RedirectTemp /17-4 https://docs.python.org/3.10/library/functions.html#iter +RedirectTemp /17-5 https://github.com/python/cpython/blob/b1930bf75f276cd7ca08c4455298128d89adf7d1/Lib/_collections_abc.py#L271 +RedirectTemp /17-6 https://github.com/python/cpython/blob/main/Lib/types.py#L6 +RedirectTemp /17-7 https://en.wikipedia.org/wiki/CLU_(programming_language) +RedirectTemp /17-8 https://docs.python.org/3/glossary.html +RedirectTemp /17-9 https://docs.python.org/3/glossary.html#term-generator-iterator +RedirectTemp /17-10 https://docs.python.org/3/glossary.html#term-generator-expression +RedirectTemp /17-11 https://marc.info/?l=python-list&m=141826925106951&w=2 +RedirectTemp /17-12 https://docs.python.org/3/library/os.html#os.walk +RedirectTemp /17-13 https://docs.python.org/3/library/itertools.html +RedirectTemp /17-14 https://docs.python.org/3/library/exceptions.html#exception-hierarchy +RedirectTemp /17-15 https://en.wikipedia.org/wiki/Depth-first_search +RedirectTemp /17-16 https://docs.python.org/3.10/library/typing.html#typing.TypeAlias +RedirectTemp /17-17 https://docs.python.org/3/library/typing.html#typing.Generator +RedirectTemp /17-18 http://www.dabeaz.com/coroutines/Coroutines.pdf +RedirectTemp /17-19 http://www.dabeaz.com/coroutines/Coroutines.pdf +RedirectTemp /17-20 https://mail.python.org/pipermail/python-ideas/2009-April/003841.html +RedirectTemp /17-21 https://mail.python.org/pipermail/python-ideas/2009-April/003912.html +RedirectTemp /17-22 https://docs.python.org/3/library/exceptions.html#StopIteration +RedirectTemp /17-23 https://docs.python.org/3/reference/expressions.html#yield-expressions +RedirectTemp /17-24 https://docs.python.org/3/reference/index.html +RedirectTemp /17-25 https://github.com/python/cpython/blob/6f743e7a4da904f61dfa84cc7d7385e4dcc79ac5/Lib/typing.py#L2060 +RedirectTemp /17-26 http://catb.org/~esr/jargon/html/G/grok.html +RedirectTemp /17-27 https://docs.python.org/3/reference/expressions.html#yieldexpr +RedirectTemp /17-28 https://docs.python.org/3/library/itertools.html +RedirectTemp /17-29 https://docs.python.org/3/library/itertools.html#itertools-recipes +RedirectTemp /17-30 https://more-itertools.readthedocs.io/en/stable/index.html +RedirectTemp /17-31 https://rittau.org/2006/11/java-iterators-are-not-iterable/ +RedirectTemp /17-32 https://docs.python.org/3/whatsnew/3.3.html#pep-380-syntax-for-delegating-to-a-subgenerator +RedirectTemp /17-33 http://www.dabeaz.com/generators/ +RedirectTemp /17-34 http://www.dabeaz.com/coroutines/ +RedirectTemp /17-35 https://archive.org/details/pyvideo_213___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-1-of-3 +RedirectTemp /17-36 https://archive.org/details/pyvideo_215___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-2-of-3 +RedirectTemp /17-37 https://archive.org/details/pyvideo_214___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-3-of-3 +RedirectTemp /17-38 http://www.dabeaz.com/finalgenerator/ +RedirectTemp /17-39 https://web.archive.org/web/20200218150637/http://seriously.dontusethiscode.com/2013/05/01/greedy-coroutine.html +RedirectTemp /17-40 https://effectivepython.com/ +RedirectTemp /17-41 https://effectivepython.com/2015/03/10/consider-coroutines-to-run-many-functions-concurrently +RedirectTemp /17-42 https://en.wikipedia.org/wiki/Conway's_Game_of_Life +RedirectTemp /17-43 https://gist.github.com/ramalho/da5590bc38c973408839 +RedirectTemp /17-44 https://gist.github.com/ramalho/da5590bc38c973408839 +RedirectTemp /17-45 https://journal.code4lib.org/articles/4893 +RedirectTemp /17-46 https://github.com/fluentpython/isis2json +RedirectTemp /17-47 https://github.com/fluentpython/isis2json/blob/master/README.rst +############################################################ 18 +RedirectTemp /18-1 https://pyvideo.org/video/1669/keynote-3/ +RedirectTemp /18-2 https://docs.python.org/3/library/sqlite3.html#using-the-connection-as-a-context-manager +RedirectTemp /18-3 https://docs.python.org/3/library/threading.html#using-locks-conditions-and-semaphores-in-the-with-statement +RedirectTemp /18-4 https://docs.python.org/3/library/decimal.html#decimal.localcontext +RedirectTemp /18-5 https://docs.python.org/3/library/unittest.mock.html#patch +RedirectTemp /18-6 https://docs.python.org/3/library/contextlib.html#contextlib.redirect_stdout +RedirectTemp /18-7 https://docs.python.org/3/library/sys.html#sys.exc_info +RedirectTemp /18-8 https://en.wikipedia.org/wiki/LL_parser +RedirectTemp /18-9 https://docs.python.org/3/library/contextlib.html +RedirectTemp /18-10 https://github.com/python/cpython/blob/8afab2ebbc1b343cd88d058914cf622fe687a2be/Lib/contextlib.py#L123 +RedirectTemp /18-11 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ +RedirectTemp /18-12 https://docs.python.org/3/library/fileinput.html#fileinput.input +RedirectTemp /18-13 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ +RedirectTemp /18-14 https://en.wikipedia.org/wiki/Euclidean_algorithm +RedirectTemp /18-15 https://github.com/fluentpython/example-code-2e/tree/master/18-with-match/lispy/py3.10/ +RedirectTemp /18-16 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lispy.py +RedirectTemp /18-17 https://github.com/fluentpython/example-code-2e/blob/6527037ae7319ba370a1ee2d9fe79214d0ed9452/18-with-match/lispy/py3.10/lis.py#L35 +RedirectTemp /18-18 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/examples_test.py +RedirectTemp /18-19 https://github.com/python/typeshed/issues/6042 +RedirectTemp /18-20 https://github.com/fluentpython/lispy/tree/main/mylis +RedirectTemp /18-21 https://github.com/fluentpython/example-code-2e/blob/00e4741926e1b771ee7c753148b1415c0bd12e39/02-array-seq/lispy/py3.10/examples_test.py +RedirectTemp /18-22 https://mitpress.mit.edu/sites/default/files/sicp/index.html +RedirectTemp /18-23 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/examples_test.py +RedirectTemp /18-24 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/lis.py +RedirectTemp /18-25 https://www.python.org/dev/peps/pep-0634/#or-patterns +RedirectTemp /18-26 https://en.wikipedia.org/wiki/Lambda#Character_encodings +RedirectTemp /18-27 https://docs.python.org/3/reference/compound_stmts.html +RedirectTemp /18-28 https://docs.python.org/3/glossary.html#term-eafp +RedirectTemp /18-29 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 +RedirectTemp /18-30 https://docs.python.org/3/reference/compound_stmts.html +RedirectTemp /18-31 https://stackoverflow.com/questions/16138232/is-it-a-good-practice-to-use-try-except-else-in-python +RedirectTemp /18-32 https://docs.python.org/3/library/stdtypes.html#typecontextmanager +RedirectTemp /18-33 https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers +RedirectTemp /18-34 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 +RedirectTemp /18-35 https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger-1?slide=34 +RedirectTemp /18-36 https://preshing.com/20110920/the-python-with-statement-by-example/ +RedirectTemp /18-37 https://www.rath.org/on-the-beauty-of-pythons-exitstack.html +RedirectTemp /18-38 https://norvig.com/lispy.html +RedirectTemp /18-39 https://norvig.com/lispy2.html +RedirectTemp /18-40 https://github.com/norvig/pytudes +RedirectTemp /18-41 https://github.com/fluentpython/lispy +RedirectTemp /18-42 https://racket-lang.org/ +RedirectTemp /18-43 https://pyvideo.org/video/1669/keynote-3/ +RedirectTemp /18-44 https://en.wikipedia.org/wiki/Tail_call +RedirectTemp /18-45 https://2ality.com/2015/06/tail-call-optimization.html +RedirectTemp /18-46 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lis.py +RedirectTemp /18-47 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lispy.py +RedirectTemp /18-48 http://neopythonic.blogspot.com/2009/04/final-words-on-tail-calls.html +RedirectTemp /18-49 https://webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit/ +RedirectTemp /18-50 http://kangax.github.io/compat-table/es6/ +RedirectTemp /18-51 https://world.hey.com/mgmarlow/what-happened-to-proper-tail-calls-in-javascript-5494c256 +RedirectTemp /18-52 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html +RedirectTemp /18-53 https://github.com/fluentpython/lispy/blob/main/mylis/mylis_2/lis.py +RedirectTemp /18-54 https://github.com/fluentpython/lispy/tree/main/mylis +############################################################ 19 +RedirectTemp /19-1 https://go.dev/blog/waza-talk +RedirectTemp /19-2 https://en.wikipedia.org/wiki/Graphics_processing_unit +RedirectTemp /19-3 https://docs.python.org/3/library/sys.html#sys.getswitchinterval +RedirectTemp /19-4 https://docs.python.org/3/library/sys.html#sys.setswitchinterval +RedirectTemp /19-5 https://en.wikipedia.org/wiki/System_call +RedirectTemp /19-6 https://mail.python.org/pipermail/python-dev/2009-October/093356.html +RedirectTemp /19-7 http://www.dabeaz.com/finalgenerator/ +RedirectTemp /19-8 https://docs.python.org/3/library/threading.html#thread-objects +RedirectTemp /19-9 https://www.pypy.org/ +RedirectTemp /19-10 https://mail.python.org/pipermail/python-list/2009-February/675659.html +RedirectTemp /19-11 https://en.wikipedia.org/wiki/Braille_Patterns +RedirectTemp /19-12 https://docs.python.org/3/library/multiprocessing.shared_memory.html +RedirectTemp /19-13 https://docs.python.org/3/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.ShareableList +RedirectTemp /19-14 https://greenlet.readthedocs.io/en/latest/ +RedirectTemp /19-15 https://docs.sqlalchemy.org/en/14/changelog/migration_14.html#asynchronous-io-support-for-core-and-orm +RedirectTemp /19-16 https://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html +RedirectTemp /19-17 http://www.gevent.org/ +RedirectTemp /19-18 https://github.com/gevent/gevent/wiki/Projects +RedirectTemp /19-19 https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor-example +RedirectTemp /19-20 https://github.com/python/asyncio/issues/284 +RedirectTemp /19-21 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor +RedirectTemp /19-22 https://mail.python.org/archives/list/python-dev@python.org/message/JBYXQH3NV3YBF7P2HLHB5CD6V3GVTY55/ +RedirectTemp /19-23 https://docs.python.org/3/library/queue.html#queue.SimpleQueue.get +RedirectTemp /19-24 https://en.wikipedia.org/wiki/Race_condition +RedirectTemp /19-25 https://github.com/fluentpython/example-code-2e/commit/2c1230579db99738a5e5e6802063bda585f6476d +RedirectTemp /19-26 https://github.com/fluentpython/example-code-2e/blob/master/19-concurrency/primes/README.md +RedirectTemp /19-27 https://github.com/fluentpython/example-code-2e/blob/master/19-concurrency/primes/threads.py +RedirectTemp /19-28 https://en.wikipedia.org/wiki/Context_switch +RedirectTemp /19-29 http://www.gotw.ca/publications/concurrency-ddj.htm +RedirectTemp /19-30 https://www.ansible.com/ +RedirectTemp /19-31 https://saltproject.io/ +RedirectTemp /19-32 https://www.fabfile.org/ +RedirectTemp /19-33 https://engineering.fb.com/2016/05/27/production-engineering/python-in-production-engineering/ +RedirectTemp /19-34 https://jupyter.org/ +RedirectTemp /19-35 https://docs.bokeh.org/en/latest/index.html +RedirectTemp /19-36 https://www.tensorflow.org/ +RedirectTemp /19-37 https://pytorch.org/ +RedirectTemp /19-38 https://www.oreilly.com/radar/where-programming-ops-ai-and-the-cloud-are-headed-in-2021/ +RedirectTemp /19-39 https://www.youtube.com/watch?v=ods97a5Pzw0 +RedirectTemp /19-40 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy +RedirectTemp /19-41 https://modwsgi.readthedocs.io/en/master/ +RedirectTemp /19-42 https://uwsgi-docs.readthedocs.io/en/latest/ +RedirectTemp /19-43 https://unit.nginx.org/ +RedirectTemp /19-44 https://www.techatbloomberg.com/blog/configuring-uwsgi-production-deployment/ +RedirectTemp /19-45 https://www.youtube.com/watch?v=p6R1h2Nn468 +RedirectTemp /19-46 https://asgi.readthedocs.io/en/latest/index.html +RedirectTemp /19-47 https://docs.celeryproject.org/en/stable/getting-started/introduction.html +RedirectTemp /19-48 https://python-rq.org/ +RedirectTemp /19-49 https://docs.celeryproject.org/en/stable/faq.html#what-kinds-of-things-should-i-use-celery-for +RedirectTemp /19-50 https://redis.io/ +RedirectTemp /19-51 https://realpython.com/intro-to-python-threading/ +RedirectTemp /19-52 https://pymotw.com/3/concurrency.html +RedirectTemp /19-53 https://www.pearson.com/us/higher-education/program/Hellmann-Python-3-Standard-Library-by-Example-The/PGM328871.html +RedirectTemp /19-54 https://docs.python.org/3/library/multiprocessing.html#programming-guidelines +RedirectTemp /19-55 https://docs.python.org/3/library/multiprocessing.html +RedirectTemp /19-56 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ +RedirectTemp /19-57 https://link.springer.com/book/10.1007/978-1-4842-5793-7?error=cookies_not_supported&code=2ed5d61d-ae9f-4f3d-94ac-0f68cf45ea4f +RedirectTemp /19-58 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 +RedirectTemp /19-59 https://greenteapress.com/wp/semaphores/ +RedirectTemp /19-60 https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock +RedirectTemp /19-61 https://docs.python.org/3/faq/library.html#can-t-we-get-rid-of-the-global-interpreter-lock +RedirectTemp /19-62 https://www.artima.com/weblogs/viewpost.jsp?thread=214235 +RedirectTemp /19-63 http://jessenoller.com/blog/2009/02/01/python-threads-and-the-global-interpreter-lock +RedirectTemp /19-64 https://realpython.com/products/cpython-internals-book/ +RedirectTemp /19-65 http://www.dabeaz.com/GIL/ +RedirectTemp /19-66 http://www.dabeaz.com/python/UnderstandingGIL.pdf +RedirectTemp /19-67 https://bugs.python.org/issue7946#msg223110 +RedirectTemp /19-68 https://bugs.python.org/issue7946 +RedirectTemp /19-69 https://www.fullstackpython.com/ +RedirectTemp /19-70 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ +RedirectTemp /19-71 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 +RedirectTemp /19-72 https://www.packtpub.com/product/distributed-computing-with-python/9781785889691 +RedirectTemp /19-73 https://towardsdatascience.com/python-performance-and-gpus-1be860ffd58d?gi=a7d537cc2fb4 +RedirectTemp /19-74 https://instagram-engineering.com/web-service-efficiency-at-instagram-with-python-4976d078e366?gi=12a441991c88 +RedirectTemp /19-75 https://www.oreilly.com/library/view/architecture-patterns-with/9781492052197/ +RedirectTemp /19-76 https://www.cosmicpython.com/ +RedirectTemp /19-77 https://pypi.org/project/lelo/ +RedirectTemp /19-78 https://github.com/npryce/python-parallelize +RedirectTemp /19-79 https://github.com/ericsnowcurrently/multi-core-python/wiki +RedirectTemp /19-80 https://gist.github.com/markshannon/79cace3656b40e21b7021504daee950c +RedirectTemp /19-81 https://mail.python.org/archives/list/python-dev@python.org/message/YOOQZCFOKEPQ24YHWWLQSJ3RCXFMS7D7/ +RedirectTemp /19-82 https://en.wikipedia.org/wiki/Communicating_sequential_processes +RedirectTemp /19-83 https://github.com/stackless-dev/stackless/wiki +RedirectTemp /19-84 https://www.eveonline.com +RedirectTemp /19-85 https://www.ccpgames.com/ +RedirectTemp /19-86 https://stackless.readthedocs.io/en/3.6-slp/stackless-python.html#history +RedirectTemp /19-87 https://doc.pypy.org/en/latest/stackless.html +RedirectTemp /19-88 https://greenlet.readthedocs.io/en/latest/ +RedirectTemp /19-89 http://www.gevent.org/ +RedirectTemp /19-90 http://thespianpy.com/doc/ +RedirectTemp /19-91 https://pykka.readthedocs.io/en/latest/ +RedirectTemp /19-92 https://www.manning.com/books/rabbitmq-in-action +RedirectTemp /19-93 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ +RedirectTemp /19-94 https://en.wikipedia.org/wiki/OpenCL +RedirectTemp /19-95 https://media.pragprog.com/titles/pb7con/Bonus_Chapter.pdf +RedirectTemp /19-96 https://martinfowler.com/ +RedirectTemp /19-97 https://martinfowler.com/articles/patterns-of-distributed-systems/ +RedirectTemp /19-98 https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/ +RedirectTemp /19-99 https://www.oreilly.com/library/view/oscon-2016-video/9781491965153/video247021.html +RedirectTemp /19-100 https://www.oreilly.com/library/view/designing-for-scalability/9781449361556/ +RedirectTemp /19-101 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy +RedirectTemp /19-102 https://en.wikipedia.org/wiki/KISS_principle +RedirectTemp /19-103 https://www.usenix.org/conference/hotos15/workshop-program/presentation/mcsherry +############################################################ 20 +RedirectTemp /20-1 https://www.artima.com/weblogs/viewpost.jsp?thread=299551 +RedirectTemp /20-2 https://docs.python.org/3/library/http.server.html +RedirectTemp /20-3 https://www.youtube.com/watch?v=A9e9Cy1UkME +RedirectTemp /20-4 https://www.cia.gov/the-world-factbook/ +RedirectTemp /20-5 https://docs.python-requests.org/en/latest/ +RedirectTemp /20-6 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor +RedirectTemp /20-7 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed +RedirectTemp /20-8 https://docs.python.org/3/library/concurrent.futures.html +RedirectTemp /20-9 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.Executor +RedirectTemp /20-10 https://github.com/fluentpython/example-code-2e/blob/master/20-executors/getflags/flags2_common.py +RedirectTemp /20-11 https://github.com/noamraph/tqdm +RedirectTemp /20-12 https://www.youtube.com/watch?v=M8Z65tAl5l4 +RedirectTemp /20-13 https://github.com/noamraph/tqdm/blob/master/README.md +RedirectTemp /20-14 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed +RedirectTemp /20-15 https://docs.python.org/3/library/asyncio-task.html#asyncio.as_completed +RedirectTemp /20-16 https://www.cloudflare.com/ +RedirectTemp /20-17 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags +RedirectTemp /20-18 https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418 +RedirectTemp /20-19 https://en.wikipedia.org/wiki/Embarrassingly_parallel +RedirectTemp /20-20 https://pyvideo.org/video/480/pyconau-2010--the-future-is-soon/ +RedirectTemp /20-21 http://www.dabeaz.com/coroutines/ +RedirectTemp /20-22 https://en.wikipedia.org/wiki/POSIX_Threads +RedirectTemp /20-23 https://en.wikipedia.org/wiki/C_dynamic_memory_allocation +RedirectTemp /20-24 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ +RedirectTemp /20-25 https://hexdocs.pm/ecto/getting-started.html +############################################################ 21 +RedirectTemp /21-1 https://docs.python.org/3/library/asyncio.html +RedirectTemp /21-2 https://bugs.python.org/issue43216 +RedirectTemp /21-3 https://bugs.python.org/issue36921 +RedirectTemp /21-4 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.getaddrinfo +RedirectTemp /21-5 https://docs.python.org/3/library/socket.html#socket.getaddrinfo +RedirectTemp /21-6 https://docs.python.org/3.10/library/asyncio-eventloop.html#asyncio.get_event_loop +RedirectTemp /21-7 https://www.python.org/dev/peps/pep-0492/#await-expression +RedirectTemp /21-8 https://www.fluentpython.com/extra/classic-coroutines/#yield_from_meaning_sec +RedirectTemp /21-9 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags +RedirectTemp /21-10 https://magicstack.github.io/asyncpg/current/ +RedirectTemp /21-11 https://magicstack.github.io/asyncpg/current/api/index.html#transactions +RedirectTemp /21-12 https://magicstack.github.io/asyncpg/current/api/index.html#transactions +RedirectTemp /21-13 https://github.com/MagicStack/asyncpg/blob/4d39a05268ce4cc01b00458223a767542da048b8/asyncpg/transaction.py#L57 +RedirectTemp /21-14 https://magicstack.github.io/asyncpg/current/usage.html#connection-pools +RedirectTemp /21-15 https://gist.github.com/jboner/2841832 +RedirectTemp /21-16 https://en.wikipedia.org/wiki/Network-attached_storage +RedirectTemp /21-17 https://en.wikipedia.org/wiki/Semaphore_(programming) +RedirectTemp /21-18 https://en.wikipedia.org/wiki/Semaphore_(programming) +RedirectTemp /21-19 https://groups.google.com/forum/#!msg/python-tulip/PdAEtwpaJHs/7fqb-Qj2zJoJ +RedirectTemp /21-20 https://web.archive.org/web/20151209151711/http://tritarget.org/blog/2012/11/28/the-pyramid-of-doom-a-javascript-style-trap +RedirectTemp /21-21 https://stackoverflow.com/questions/53701841/what-is-the-use-case-for-future-add-done-callback/53710563 +RedirectTemp /21-22 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor +RedirectTemp /21-23 https://motor.readthedocs.io/en/stable/ +RedirectTemp /21-24 https://emptysqua.re/blog/response-to-asynchronous-python-and-databases/ +RedirectTemp /21-25 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams +RedirectTemp /21-26 https://en.wikipedia.org/wiki/Phaistos_Disc +RedirectTemp /21-27 https://en.wikipedia.org/wiki/Inverted_index +RedirectTemp /21-28 https://fastapi.tiangolo.com/ +RedirectTemp /21-29 https://swagger.io/specification/ +RedirectTemp /21-30 https://asgi.readthedocs.io/en/latest/implementations.html +RedirectTemp /21-31 https://pydantic-docs.helpmanual.io/ +RedirectTemp /21-32 https://doc.traefik.io/traefik/ +RedirectTemp /21-33 https://fastapi.tiangolo.com/project-generation/ +RedirectTemp /21-34 https://fastapi.tiangolo.com/tutorial/response-model/ +RedirectTemp /21-35 https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server +RedirectTemp /21-36 https://github.com/python/typeshed/issues/5535 +RedirectTemp /21-37 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Server.serve_forever +RedirectTemp /21-38 https://docs.python.org/3/library/asyncio-stream.html#asyncio.StreamWriter.close +RedirectTemp /21-39 https://docs.python.org/3/library/asyncio-stream.html#streamwriter +RedirectTemp /21-40 https://docs.python.org/3/library/asyncio-stream.html +RedirectTemp /21-41 https://docs.python.org/3/library/asyncio-protocol.html +RedirectTemp /21-42 https://docs.python.org/3/library/asyncio-protocol.html#tcp-echo-server +RedirectTemp /21-43 https://github.com/aio-libs/aiopg +RedirectTemp /21-44 https://docs.python.org/3/whatsnew/3.8.html#asyncio +RedirectTemp /21-45 https://datatracker.ietf.org/doc/html/rfc6761 +RedirectTemp /21-46 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager +RedirectTemp /21-47 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager +RedirectTemp /21-48 https://docs.python.org/3/library/asyncio-task.html#asyncio.gather +RedirectTemp /21-49 https://curio.readthedocs.io/en/latest/index.html +RedirectTemp /21-50 https://curio.readthedocs.io/en/latest/reference.html#task-groups +RedirectTemp /21-51 https://en.wikipedia.org/wiki/Structured_concurrency +RedirectTemp /21-52 https://mail.python.org/archives/list/python-dev@python.org/thread/2ORDAW74LGE3ZI2QETPJRT2ZL7MCCPG2/ +RedirectTemp /21-53 https://www.python.org/dev/peps/pep-0654/#motivation +RedirectTemp /21-54 https://curio.readthedocs.io/en/latest/reference.html#AWAIT +RedirectTemp /21-55 https://www.python-httpx.org/async/#curio +RedirectTemp /21-56 https://github.com/dabeaz/curio/tree/78bca8a6ad677ef51e1568ac7b3e51441ab49c42/examples +RedirectTemp /21-57 https://datatracker.ietf.org/doc/html/rfc8305 +RedirectTemp /21-58 https://trio.readthedocs.io/en/stable/ +RedirectTemp /21-59 https://www.youtube.com/watch?v=M-sc73Y-zQA +RedirectTemp /21-60 https://en.wikipedia.org/wiki/Technical_debt +RedirectTemp /21-61 https://www.youtube.com/watch?v=E-1Y4kSsAFc +RedirectTemp /21-62 https://github.com/dabeaz/curio +RedirectTemp /21-63 https://trio.readthedocs.io/en/stable/ +RedirectTemp /21-64 https://curio.readthedocs.io/en/latest/#curio-university +RedirectTemp /21-65 https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/ +RedirectTemp /21-66 https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ +RedirectTemp /21-67 https://stackoverflow.com/questions/49482969/what-is-the-core-difference-between-asyncio-and-trio +RedirectTemp /21-68 https://docs.python.org/3/library/asyncio.html +RedirectTemp /21-69 https://bugs.python.org/issue33649 +RedirectTemp /21-70 https://docs.python.org/3/library/asyncio-dev.html +RedirectTemp /21-71 https://www.youtube.com/watch?v=iG6fr81xHKA +RedirectTemp /21-72 https://www.youtube.com/watch?v=F19R_M4Nay4 +RedirectTemp /21-73 https://asherman.io/projects/unsync.html +RedirectTemp /21-74 https://pyladies.com/ +RedirectTemp /21-75 https://www.youtube.com/watch?v=sW76-pRkZk8 +RedirectTemp /21-76 https://www.youtube.com/watch?v=Xbl7XjFYsN4 +RedirectTemp /21-77 https://www.youtube.com/watch?v=02CLD-42VdI +RedirectTemp /21-78 https://micropython.org/ +RedirectTemp /21-79 https://docs.micropython.org/en/latest/library/uasyncio.html +RedirectTemp /21-80 https://www.encode.io/articles/python-async-frameworks-beyond-developer-tribalism +RedirectTemp /21-81 https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ +RedirectTemp /21-82 https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ +RedirectTemp /21-83 https://github.com/MagicStack/uvloop +RedirectTemp /21-84 http://magic.io/blog/uvloop-blazing-fast-python-networking/ +RedirectTemp /21-85 https://github.com/MagicStack/httptools +RedirectTemp /21-86 https://docs.aiohttp.org/en/stable/ +RedirectTemp /21-87 https://github.com/wg/wrk +RedirectTemp /21-88 https://twistedmatrix.com/trac/ +############################################################ 22 +RedirectTemp /22-1 https://github.com/fluentpython/example-code-2e/blob/master/22-dyn-attr-prop/oscon/data/osconfeed.json +RedirectTemp /22-2 https://pypi.org/project/attrdict/ +RedirectTemp /22-3 https://pypi.org/project/addict/ +RedirectTemp /22-4 https://github.com/ActiveState/code/tree/master/recipes/Python/52308_simple_but_handy_collector_bunch_named_stuff +RedirectTemp /22-5 https://docs.python.org/3/library/types.html#types.SimpleNamespace +RedirectTemp /22-6 https://docs.python.org/3/library/argparse.html#argparse.Namespace +RedirectTemp /22-7 https://docs.python.org/3/library/multiprocessing.html#multiprocessing.managers.Namespace +RedirectTemp /22-8 https://github.com/fluentpython/example-code-2e/blob/master/22-dyn-attr-prop/oscon/schedule_v2.py +RedirectTemp /22-9 https://docs.python.org/3/library/functools.html#functools.cached_property +RedirectTemp /22-10 https://docs.python.org/3/library/functools.html#functools.cached_property +RedirectTemp /22-11 https://bugs.python.org/issue42781 +RedirectTemp /22-12 https://docs.python.org/3/howto/descriptor.html +RedirectTemp /22-13 https://github.com/python/cpython/blob/e6d0107e13ed957109e79b796984d3d026a8660d/Lib/functools.py#L926 +RedirectTemp /22-14 https://docs.python.org/3/library/threading.html#rlock-objects +RedirectTemp /22-15 https://docs.python.org/3.10/library/functools.html#functools.cached_property +RedirectTemp /22-16 https://www.wsj.com/articles/SB10001424052970203914304576627102996831200 +RedirectTemp /22-17 https://www.youtube.com/watch?v=s35rVw1zskA&feature=youtu.be +RedirectTemp /22-18 https://docs.python.org/3/library/functions.html#dir +RedirectTemp /22-19 https://github.com/python/cpython/blob/19903085c3ad7a17c8047e1556c700f2eb109931/Lib/cmd.py#L214 +RedirectTemp /22-20 https://docs.python.org/3/library/functions.html#hasattr +RedirectTemp /22-21 https://docs.python.org/3.10/reference/datamodel.html#special-method-lookup +RedirectTemp /22-22 https://docs.python.org/3/library/functions.html +RedirectTemp /22-23 https://docs.python.org/3/reference/datamodel.html#customizing-attribute-access +RedirectTemp /22-24 https://docs.python.org/3/reference/datamodel.html#special-method-lookup +RedirectTemp /22-25 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /22-26 http://wiki.c2.com/?WelcomeVisitors +RedirectTemp /22-27 http://wiki.c2.com/?UniformAccessPrinciple +RedirectTemp /22-28 https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html +RedirectTemp /22-29 http://www.pingo.io/docs/ +RedirectTemp /22-30 https://www.drdobbs.com/javas-new-considered-harmful/184405016 +RedirectTemp /22-31 https://www.python.org/dev/peps/pep-0008/#class-names +############################################################ 23 +RedirectTemp /23-1 http://www.aleax.it/goo_pydp.pdf +RedirectTemp /23-2 https://docs.python.org/3.10/reference/datamodel.html#implementing-descriptors +RedirectTemp /23-3 https://docs.python.org/3/howto/descriptor.html +RedirectTemp /23-4 https://docs.python.org/3/howto/ +RedirectTemp /23-5 http://www.aleax.it/Python/nylug05_om.pdf +RedirectTemp /23-6 https://www.youtube.com/watch?v=VOzvpHoYQoo +RedirectTemp /23-7 https://www.python.org/dev/peps/pep-0487/#trait-descriptors +RedirectTemp /23-8 https://dreamsongs.com/RiseOfWorseIsBetter.html +RedirectTemp /23-9 http://web.archive.org/web/20031002184114/www.amk.ca/python/writing/warts.html +RedirectTemp /23-10 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this +RedirectTemp /23-11 http://python-history.blogspot.com/2009/02/adding-support-for-user-defined-classes.html +############################################################ 24 +RedirectTemp /24-1 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /24-2 https://docs.djangoproject.com/en/3.2/topics/db/models/#meta-options +RedirectTemp /24-3 https://www.python.org/dev/peps/pep-3155/ +RedirectTemp /24-4 https://github.com/python/cpython/blob/3.9/Lib/collections/__init__.py +RedirectTemp /24-5 https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions +RedirectTemp /24-6 https://go.dev/tour/basics/12 +RedirectTemp /24-7 https://bugs.python.org/issue42102 +RedirectTemp /24-8 https://docs.python.org/3/reference/datamodel.html#object.__init_subclass__ +RedirectTemp /24-9 https://www.python.org/dev/peps/pep-0557/#abstract +RedirectTemp /24-10 https://github.com/python/cpython/blob/3.9/Lib/dataclasses.py +RedirectTemp /24-11 https://docs.python.org/3/reference/datamodel.html#creating-the-class-object +RedirectTemp /24-12 https://mail.python.org/pipermail/python-list/2002-December/134521.html +RedirectTemp /24-13 https://mail.python.org/pipermail/python-list/2002-July/162558.html +RedirectTemp /24-14 https://github.com/fluentpython/example-code/tree/master/21-class-metaprog/bulkfood +RedirectTemp /24-15 https://en.wikipedia.org/wiki/Principle_of_least_astonishment +RedirectTemp /24-16 https://docs.python.org/3/reference/datamodel.html#object.__class_getitem__ +RedirectTemp /24-17 https://en.wikipedia.org/wiki/Trait_(computer_programming) +RedirectTemp /24-18 https://en.wikipedia.org/wiki/Aspect-oriented_programming +RedirectTemp /24-19 https://dhh.dk/arc/000416.html +RedirectTemp /24-20 https://github.com/cjrh/autoslot +RedirectTemp /24-21 https://docs.python.org/3/reference/datamodel.html#customizing-class-creation +RedirectTemp /24-22 https://docs.python.org/3/library/functions.html#type +RedirectTemp /24-23 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /24-24 https://docs.python.org/3/library/types.html +RedirectTemp /24-25 https://www.python.org/dev/peps/pep-3129/ +RedirectTemp /24-26 https://www.youtube.com/watch?v=cAGliEJV9_o +RedirectTemp /24-27 https://docs.python.org/3/library/functools.html#functools.total_ordering +RedirectTemp /24-28 https://www.python.org/download/releases/2.2.3/descrintro/ +RedirectTemp /24-29 https://github.com/lihaoyi/macropy +RedirectTemp /24-30 https://people.eecs.berkeley.edu/~bh/ss-toc2.html +# content of short.htaccess file created and managed by short.py + +# appended: 2025-05-23 15:12:13 +RedirectTemp /22 https://pythonfluente.com/2/#pattern_matching_case_study_sec +RedirectTemp /23 https://pythonfluente.com/2/#how_slicing_works +RedirectTemp /24 https://pythonfluente.com/2/#sliceable_sequence +RedirectTemp /25 https://pythonfluente.com/2/#virtual_subclass_sec +RedirectTemp /26 https://pythonfluente.com/2/#environment_class_ex +RedirectTemp /27 https://pythonfluente.com/2/#subclass_builtin_woes +RedirectTemp /28 https://pythonfluente.com/2/#slots_section +RedirectTemp /29 https://pythonfluente.com/2/#typeddict_sec +RedirectTemp /2a https://pythonfluente.com/2/#problems_annot_runtime_sec +RedirectTemp /2b https://pythonfluente.com/2/#legacy_deprecated_typing_box +RedirectTemp /2c https://pythonfluente.com/2/#positional_pattern_implement_sec diff --git a/links/short.htaccess b/links/short.htaccess index 6b09a50..86fe3e2 100644 --- a/links/short.htaccess +++ b/links/short.htaccess @@ -1 +1,14 @@ # content of short.htaccess file created and managed by short.py + +# appended: 2025-05-23 15:12:13 +RedirectTemp /22 https://pythonfluente.com/2/#pattern_matching_case_study_sec +RedirectTemp /23 https://pythonfluente.com/2/#how_slicing_works +RedirectTemp /24 https://pythonfluente.com/2/#sliceable_sequence +RedirectTemp /25 https://pythonfluente.com/2/#virtual_subclass_sec +RedirectTemp /26 https://pythonfluente.com/2/#environment_class_ex +RedirectTemp /27 https://pythonfluente.com/2/#subclass_builtin_woes +RedirectTemp /28 https://pythonfluente.com/2/#slots_section +RedirectTemp /29 https://pythonfluente.com/2/#typeddict_sec +RedirectTemp /2a https://pythonfluente.com/2/#problems_annot_runtime_sec +RedirectTemp /2b https://pythonfluente.com/2/#legacy_deprecated_typing_box +RedirectTemp /2c https://pythonfluente.com/2/#positional_pattern_implement_sec From cf3161ca006b106bfc0ba698e9135e9cdfb51e55 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Wed, 4 Jun 2025 18:54:30 -0300 Subject: [PATCH 127/127] moved URL shortening code to https://github.com/pythonfluente/pythonfluente2e --- links/README.md | 54 +-- links/custom.htaccess | 1072 ----------------------------------------- links/deploy.sh | 2 - links/sample-urls.txt | 47 -- links/short.htaccess | 14 - links/short.py | 95 ---- 6 files changed, 4 insertions(+), 1280 deletions(-) delete mode 100644 links/custom.htaccess delete mode 100755 links/deploy.sh delete mode 100644 links/sample-urls.txt delete mode 100644 links/short.htaccess delete mode 100755 links/short.py diff --git a/links/README.md b/links/README.md index 2a3d80a..65749b2 100644 --- a/links/README.md +++ b/links/README.md @@ -1,52 +1,6 @@ -# Short links for URLs in the book +This file is deployed as `.htaccess` to the FPY.LI domain +to map short URLs in Fluent Python to the original URLs. -## Problem: link rot +To update it, I use tools in this other repo: -_Fluent Python, Second Edition_ has more than 1000 links to external resources. -Inevitably, some of those links will rot as time passes. -But I can't change the URLs in the print book... - -## Solution: indirection - -I replaced almost all URLs in the book with shortened versions that go through the `fpy.li` site which I control. -The site has an `.htaccess` file with *temporary* redirects. - -When I find out a link is stale, I can thange the redirect in `.htaccess` to a new target, -which may be a link to copy in the Internet Archive's -[Wayback Machine](https://archive.org/web/) -o the link in the book is back in service through the updated redirect. - - -## Help wanted - -Please report broken links as bugs in the [`FPY.LI.htaccess`](FPY.LI.htaccess) file. -Also, feel free to send pull requests with fixes to that file. -When I accept a PR, I will redeploy it to `fpy.li/.htaccess`. - - -## Details - -Almost all URLs in the book are replaced with shortened versions like -[`http://fpy.li/1-3`](http://fpy.li/1-3)—for chapter 1, link #3. - -There are also custom short URLs like -[`https://fpy.li/code`](https://fpy.li/code) which redirects to the example code repository. -I used custom short URLs for URLs with 3 or more mentions, or links to PEPs. - -Exceptions: - -- URLs with `oreilly` in them are unchanged; -- `fluentpython.com` URL (with no path) is unchanged; - -The `custom.htaccess` file contains redirects with custom names -plus numbered URLs generated from the links in each chapter in -the Second Edition in English. - -`short.htaccess` has redirects made by `short.py`, starting -with the Second Edition in Brazilian Portuguese. - -```shell -cat custom.htaccess short.htaccess > FPY.LI.htaccess -``` - -`FPY.LI.htaccess` is deployed at the root folder in `http://fpy.li`. +https://github.com/pythonfluente/pythonfluente2e diff --git a/links/custom.htaccess b/links/custom.htaccess deleted file mode 100644 index cc779db..0000000 --- a/links/custom.htaccess +++ /dev/null @@ -1,1072 +0,0 @@ -ErrorDocument 404 /404.html - -# main resources -RedirectTemp /book https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ -RedirectTemp /code https://github.com/fluentpython/example-code-2e -RedirectTemp /home https://www.fluentpython.com/ - -# URLs mentioned at least three times -RedirectTemp /bisect https://www.fluentpython.com/extra/ordered-sequences-with-bisect/ -RedirectTemp /cardxvi https://www.python.org/dev/peps/pep-0484/#the-numeric-tower -RedirectTemp /collec https://docs.python.org/3/library/collections.html -RedirectTemp /dask https://dask.org/ -RedirectTemp /dtmodel https://docs.python.org/3/reference/datamodel.html -RedirectTemp /descr101 https://www.python.org/download/releases/2.2.3/descrintro/ -RedirectTemp /descrhow https://docs.python.org/3/howto/descriptor.html -RedirectTemp /doctest https://docs.python.org/3/library/doctest.html -RedirectTemp /effectpy https://effectivepython.com/ -RedirectTemp /fmtspec https://docs.python.org/3/library/string.html#formatspec -RedirectTemp /gunicorn https://gunicorn.org/ -RedirectTemp /hashint https://www.fluentpython.com/extra/internals-of-sets-and-dicts/ -RedirectTemp /hattingh https://www.oreilly.com/library/view/using-asyncio-in/9781492075325/ -RedirectTemp /httpx https://www.python-httpx.org/ -RedirectTemp /initvar https://docs.python.org/3/library/dataclasses.html#init-only-variables -RedirectTemp /mypy https://mypy.readthedocs.io/en/stable/ -RedirectTemp /norvigdp http://norvig.com/design-patterns/ -RedirectTemp /nsphere https://en.wikipedia.org/wiki/N-sphere -RedirectTemp /oldcoro https://www.fluentpython.com/extra/classic-coroutines/ -RedirectTemp /pandas https://pandas.pydata.org/ -RedirectTemp /pycook3 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ -RedirectTemp /pynut3 https://www.oreilly.com/library/view/python-in-a/9781491913833/ -RedirectTemp /pypydif https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types -RedirectTemp /shed4051 https://github.com/python/typeshed/issues/4051 -RedirectTemp /specattr https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine -RedirectTemp /typing https://docs.python.org/3/library/typing.html -RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ - -# URL added during QA of the Second Edition -RedirectTemp /bdfl https://www.artima.com/weblogs/viewpost.jsp?thread=235725 - -# Python Enhancement Proposals -RedirectTemp /pep218 https://www.python.org/dev/peps/pep-0218/ -RedirectTemp /pep227 https://www.python.org/dev/peps/pep-0227/ -RedirectTemp /pep255 https://www.python.org/dev/peps/pep-0255/ -RedirectTemp /pep342 https://www.python.org/dev/peps/pep-0342/ -RedirectTemp /pep343 https://www.python.org/dev/peps/pep-0343/ -RedirectTemp /pep357 https://www.python.org/dev/peps/pep-0357/ -RedirectTemp /pep362 https://www.python.org/dev/peps/pep-0362/ -RedirectTemp /pep371 https://www.python.org/dev/peps/pep-0371/ -RedirectTemp /pep380 https://www.python.org/dev/peps/pep-0380/ -RedirectTemp /pep393 https://www.python.org/dev/peps/pep-0393/ -RedirectTemp /pep412 https://www.python.org/dev/peps/pep-0412/ -RedirectTemp /pep442 https://www.python.org/dev/peps/pep-0442/ -RedirectTemp /pep443 https://www.python.org/dev/peps/pep-0443/ -RedirectTemp /pep448 https://www.python.org/dev/peps/pep-0448/ -RedirectTemp /pep455 https://www.python.org/dev/peps/pep-0455/ -RedirectTemp /pep456 https://www.python.org/dev/peps/pep-0456/ -RedirectTemp /pep461 https://www.python.org/dev/peps/pep-0461/ -RedirectTemp /pep465 https://www.python.org/dev/peps/pep-0465/ -RedirectTemp /pep467 https://www.python.org/dev/peps/pep-0467/ -RedirectTemp /pep482 https://www.python.org/dev/peps/pep-0482/ -RedirectTemp /pep483 https://www.python.org/dev/peps/pep-0483/ -RedirectTemp /pep484 https://www.python.org/dev/peps/pep-0484/ -RedirectTemp /pep487 https://www.python.org/dev/peps/pep-0487/ -RedirectTemp /pep492 https://www.python.org/dev/peps/pep-0492/ -RedirectTemp /pep519 https://www.python.org/dev/peps/pep-0519/ -RedirectTemp /pep525 https://www.python.org/dev/peps/pep-0525/ -RedirectTemp /pep526 https://www.python.org/dev/peps/pep-0526/ -RedirectTemp /pep528 https://www.python.org/dev/peps/pep-0528/ -RedirectTemp /pep529 https://www.python.org/dev/peps/pep-0529/ -RedirectTemp /pep530 https://www.python.org/dev/peps/pep-0530/ -RedirectTemp /pep544 https://www.python.org/dev/peps/pep-0544/ -RedirectTemp /pep554 https://www.python.org/dev/peps/pep-0554/ -RedirectTemp /pep557 https://www.python.org/dev/peps/pep-0557/ -RedirectTemp /pep560 https://www.python.org/dev/peps/pep-0560/ -RedirectTemp /pep561 https://www.python.org/dev/peps/pep-0561/ -RedirectTemp /pep563 https://www.python.org/dev/peps/pep-0563/ -RedirectTemp /pep570 https://www.python.org/dev/peps/pep-0570/ -RedirectTemp /pep572 https://www.python.org/dev/peps/pep-0572/ -RedirectTemp /pep584 https://www.python.org/dev/peps/pep-0584/ -RedirectTemp /pep585 https://www.python.org/dev/peps/pep-0585/ -RedirectTemp /pep586 https://www.python.org/dev/peps/pep-0586/ -RedirectTemp /pep589 https://www.python.org/dev/peps/pep-0589/ -RedirectTemp /pep591 https://www.python.org/dev/peps/pep-0591/ -RedirectTemp /pep593 https://www.python.org/dev/peps/pep-0593/ -RedirectTemp /pep604 https://www.python.org/dev/peps/pep-0604/ -RedirectTemp /pep612 https://www.python.org/dev/peps/pep-0612/ -RedirectTemp /pep613 https://www.python.org/dev/peps/pep-0613/ -RedirectTemp /pep616 https://www.python.org/dev/peps/pep-0616/ -RedirectTemp /pep617 https://www.python.org/dev/peps/pep-0617/ -RedirectTemp /pep618 https://www.python.org/dev/peps/pep-0618/ -RedirectTemp /pep634 https://www.python.org/dev/peps/pep-0634/ -RedirectTemp /pep635 https://www.python.org/dev/peps/pep-0635/ -RedirectTemp /pep636 https://www.python.org/dev/peps/pep-0636/ -RedirectTemp /pep638 https://www.python.org/dev/peps/pep-0638/ -RedirectTemp /pep645 https://www.python.org/dev/peps/pep-0645/ -RedirectTemp /pep646 https://www.python.org/dev/peps/pep-0646/ -RedirectTemp /pep647 https://www.python.org/dev/peps/pep-0647/ -RedirectTemp /pep649 https://www.python.org/dev/peps/pep-0649/ -RedirectTemp /pep654 https://www.python.org/dev/peps/pep-0654/ -RedirectTemp /pep655 https://www.python.org/dev/peps/pep-0655/ -RedirectTemp /pep661 https://www.python.org/dev/peps/pep-0661/ -RedirectTemp /pep3099 https://www.python.org/dev/peps/pep-3099/ -RedirectTemp /pep3102 https://www.python.org/dev/peps/pep-3102/ -RedirectTemp /pep3104 https://www.python.org/dev/peps/pep-3104/ -RedirectTemp /pep3106 https://www.python.org/dev/peps/pep-3106/ -RedirectTemp /pep3107 https://www.python.org/dev/peps/pep-3107/ -RedirectTemp /pep3115 https://www.python.org/dev/peps/pep-3115/ -RedirectTemp /pep3118 https://www.python.org/dev/peps/pep-3118/ -RedirectTemp /pep3119 https://www.python.org/dev/peps/pep-3119/ -RedirectTemp /pep3129 https://www.python.org/dev/peps/pep-3129/ -RedirectTemp /pep3132 https://www.python.org/dev/peps/pep-3132/ -RedirectTemp /pep3141 https://www.python.org/dev/peps/pep-3141/ -RedirectTemp /pep3148 https://www.python.org/dev/peps/pep-3148/ -RedirectTemp /pep3155 https://www.python.org/dev/peps/pep-3155/ -RedirectTemp /pep3333 https://www.python.org/dev/peps/pep-3333/ - -# Remaining URLs by chapter - -############################################################ p -RedirectTemp /p-1 https://mail.python.org/pipermail/python-list/2002-December/134521.html -RedirectTemp /p-2 https://docs.python.org/3.10/tutorial/ -RedirectTemp /p-3 https://docs.python.org/3/tutorial/ -RedirectTemp /p-4 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ -RedirectTemp /p-5 https://www.oreilly.com/online-learning/try-now.html -RedirectTemp /p-6 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ -RedirectTemp /p-7 https://stackoverflow.com/users/95810/alex-martelli -RedirectTemp /p-8 https://pythonpro.com.br -RedirectTemp /p-9 https://groups.google.com/g/python-brasil -RedirectTemp /p-10 https://www.coffeelab.com.br/ -RedirectTemp /p-11 https://garoa.net.br/wiki/P%C3%A1gina_principal -############################################################ a -RedirectTemp /a-1 https://groups.google.com/forum/#!topic/python-tulip/Y4bhLNbKs74 -RedirectTemp /a-2 https://docs.python.org/3/library/asyncio-eventloop.html#executor -RedirectTemp /a-3 https://www.youtube.com/watch?v=x-kB2o8sd5c -RedirectTemp /a-4 https://www.youtube.com/watch?v=OSGv2VnC0go -RedirectTemp /a-5 https://mail.python.org/pipermail/python-ideas/2015-March/032557.html -RedirectTemp /a-6 https://pypi.org/project/pep8/ -RedirectTemp /a-7 https://pypi.org/project/flake8/ -RedirectTemp /a-8 https://pypi.org/project/pyflakes/ -RedirectTemp /a-9 https://pypi.org/project/mccabe/ -RedirectTemp /a-10 https://google.github.io/styleguide/pyguide.html -RedirectTemp /a-11 https://flask.palletsprojects.com/en/1.1.x/styleguide/ -RedirectTemp /a-12 https://docs.python-guide.org/ -RedirectTemp /a-13 https://david.goodger.org/projects/pycon/2007/idiomatic/handout.html -RedirectTemp /a-14 https://docs.mongodb.com/manual/about/#about-the-documentation-process -RedirectTemp /a-15 https://blog.startifact.com/posts/older/what-is-pythonic.html -RedirectTemp /a-16 https://mail.python.org/pipermail/tutor/2003-October/thread.html#25930 -RedirectTemp /a-17 https://mail.python.org/pipermail/python-list/2003-April/192027.html -RedirectTemp /a-18 https://www.python.org/doc/essays/ -############################################################ 01 -RedirectTemp /1-1 http://hugunin.net/story_of_jython.html -RedirectTemp /1-2 https://www.oreilly.com/library/view/jython-essentials/9781449397364/ -RedirectTemp /1-3 https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers -RedirectTemp /1-4 https://docs.python.org/3.10/library/string.html#format-string-syntax -RedirectTemp /1-5 https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr -RedirectTemp /1-6 https://docs.python.org/3/library/stdtypes.html#truth -RedirectTemp /1-7 https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists -RedirectTemp /1-8 https://www.python.org/doc/humor/#the-zen-of-python -RedirectTemp /1-9 https://stackoverflow.com/users/95810/alex-martelli -RedirectTemp /1-10 https://en.wikipedia.org/wiki/Object_model -RedirectTemp /1-11 https://www.dourish.com/goodies/jargon.html -RedirectTemp /1-12 https://zopeinterface.readthedocs.io/en/latest/ -RedirectTemp /1-13 https://plone.org/ -############################################################ 02 -RedirectTemp /2-1 https://github.com/fluentpython/example-code-2e/blob/master/02-array-seq/listcomp_speed.py -RedirectTemp /2-2 https://www.python.org/dev/peps/pep-3132/ -RedirectTemp /2-3 https://stackoverflow.com/questions/68630/are-tuples-more-efficient-than-lists-in-python/22140115#22140115 -RedirectTemp /2-4 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations -RedirectTemp /2-5 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations -RedirectTemp /2-6 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching -RedirectTemp /2-7 https://docs.python.org/3.10/whatsnew/3.10.html -RedirectTemp /2-8 https://en.wikipedia.org/wiki/Switch_statement#Fallthrough -RedirectTemp /2-9 https://en.wikipedia.org/wiki/Dangling_else -RedirectTemp /2-10 https://github.com/gvanrossum/patma/blob/3ece6444ef70122876fd9f0099eb9490a2d630df/EXAMPLES.md#case-6-a-very-deep-iterable-and-type-match-with-extraction -RedirectTemp /2-11 https://github.com/fluentpython/lispy/blob/main/original/norvig/lis.py -RedirectTemp /2-12 https://norvig.com/lispy.html -RedirectTemp /2-13 https://numpy.org/doc/stable/user/quickstart.html#indexing-slicing-and-iterating -RedirectTemp /2-14 https://pythontutor.com/ -RedirectTemp /2-15 https://en.wikipedia.org/wiki/Fluent_interface -RedirectTemp /2-16 https://docs.python.org/3/library/bisect.html#bisect.insort -RedirectTemp /2-17 https://stackoverflow.com/questions/4845418/when-should-a-memoryview-be-used/ -RedirectTemp /2-18 https://www.fluentpython.com/extra/parsing-binary-struct/ -RedirectTemp /2-19 http://www.netlib.org -RedirectTemp /2-20 https://pandas.pydata.org/ -RedirectTemp /2-21 https://scikit-learn.org/stable/ -RedirectTemp /2-22 https://docs.python.org/3/howto/sorting.html -RedirectTemp /2-23 https://www.python.org/dev/peps/pep-3132/ -RedirectTemp /2-24 https://bugs.python.org/issue2292 -RedirectTemp /2-25 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching -RedirectTemp /2-26 https://docs.python.org/3.10/whatsnew/3.10.html -RedirectTemp /2-27 https://www.python.org/dev/peps/pep-0636/#appendix-a-quick-intro -RedirectTemp /2-28 https://eli.thegreenplace.net/2011/11/28/less-copies-in-python-with-the-buffer-protocol-and-memoryviews/ -RedirectTemp /2-29 https://jakevdp.github.io/PythonDataScienceHandbook/ -RedirectTemp /2-30 https://www.oreilly.com/library/view/python-for-data/9781491957653/ -RedirectTemp /2-31 https://www.labri.fr/perso/nrougier/from-python-to-numpy/ -RedirectTemp /2-32 https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html -RedirectTemp /2-33 http://www.fonts101.com/fonts/view/Uncategorized/34398/Dijkstra -RedirectTemp /2-34 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types -RedirectTemp /2-35 https://en.wikipedia.org/wiki/Timsort -RedirectTemp /2-36 http://www.groklaw.net/pdf3/OraGoogle-1202.pdf -RedirectTemp /2-37 https://www.python.org/doc/humor/#id9 -############################################################ 03 -RedirectTemp /3-1 https://www.python.org/dev/peps/pep-0584/#motivation -RedirectTemp /3-2 https://docs.python.org/3.10/c-api/typeobj.html#Py_TPFLAGS_MAPPING -RedirectTemp /3-3 https://docs.python.org/3/glossary.html#term-hashable -RedirectTemp /3-4 https://docs.python.org/3/glossary.html#term-hashable -RedirectTemp /3-5 http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf -RedirectTemp /3-6 https://github.com/pingo-io/pingo-py -RedirectTemp /3-7 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/missing.py -RedirectTemp /3-8 https://docs.python.org/3/library/collections.html#collections.ChainMap -RedirectTemp /3-9 https://docs.python.org/3/library/collections.html#collections.Counter -RedirectTemp /3-10 https://docs.python.org/3/library/shelve.html -RedirectTemp /3-11 https://docs.python.org/3/library/dbm.html -RedirectTemp /3-12 https://docs.python.org/3/library/pickle.html -RedirectTemp /3-13 https://nedbatchelder.com/blog/202006/pickles_nine_flaws.html -RedirectTemp /3-14 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py#L813 -RedirectTemp /3-15 https://mail.python.org/pipermail/python-dev/2015-May/140003.html -RedirectTemp /3-16 https://bugs.python.org/issue18986 -RedirectTemp /3-17 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/transformdict.py -RedirectTemp /3-18 http://gandenberger.org/2018/03/10/ordered-dicts-vs-ordereddict/ -RedirectTemp /3-19 https://www.pypy.org/ -RedirectTemp /3-20 https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html -RedirectTemp /3-21 https://www.npopov.com/2014/12/22/PHPs-new-hashtable-implementation.html -RedirectTemp /3-22 https://www.youtube.com/watch?v=66P5FMkWoVU -RedirectTemp /3-23 https://pyvideo.org/video/276/the-mighty-dictionary-55/ -RedirectTemp /3-24 https://www.youtube.com/watch?v=p33CVV29OG8 -RedirectTemp /3-25 https://docs.python.org/3/whatsnew/3.6.html#new-dict-implementation -RedirectTemp /3-26 https://github.com/python/cpython/blob/cf7eaa4617295747ee5646c4e2b7e7a16d7c64ab/Objects/dictobject.c -RedirectTemp /3-27 https://github.com/python/cpython/blob/cf7eaa4617295747ee5646c4e2b7e7a16d7c64ab/Objects/dictnotes.txt -RedirectTemp /3-28 https://www.youtube.com/watch?v=tGAngdU_8D8 -RedirectTemp /3-29 https://speakerdeck.com/ramalho/python-set-practice-at-pycon -RedirectTemp /3-30 https://github.com/standupdev/uintset -RedirectTemp /3-31 https://spectrum.ieee.org/hans-peter-luhn-and-the-birth-of-the-hashing-algorithm -RedirectTemp /3-32 http://www.json.org/fatfree.html -RedirectTemp /3-33 https://twitter.com/mitsuhiko/status/1229385843585974272 -############################################################ 04 -RedirectTemp /4-1 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 -RedirectTemp /4-2 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ -RedirectTemp /4-3 https://www.fluentpython.com/extra/parsing-binary-struct/ -RedirectTemp /4-4 https://www.fluentpython.com/extra/multi-character-emojis/ -RedirectTemp /4-5 https://w3techs.com/technologies/overview/character_encoding -RedirectTemp /4-6 https://docs.python.org/3/library/codecs.html#codecs.register_error -RedirectTemp /4-7 https://docs.python.org/3/library/stdtypes.html#str.isascii -RedirectTemp /4-8 https://pypi.org/project/chardet/ -RedirectTemp /4-9 https://docs.python.org/3/library/codecs.html#encodings-and-unicode -RedirectTemp /4-10 https://nedbatchelder.com/text/unipain/unipain.html -RedirectTemp /4-11 https://devblogs.microsoft.com/commandline/windows-command-line-unicode-and-utf-8-output-text-buffer/ -RedirectTemp /4-12 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONIOENCODING -RedirectTemp /4-13 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONLEGACYWINDOWSSTDIO -RedirectTemp /4-14 https://docs.python.org/3/library/locale.html#locale.getpreferredencoding -RedirectTemp /4-15 http://www.w3.org/TR/charmod-norm/ -RedirectTemp /4-16 https://docs.python.org/3/library/locale.html?highlight=strxfrm#locale.strxfrm -RedirectTemp /4-17 https://pypi.org/project/pyuca/ -RedirectTemp /4-18 https://github.com/jtauber/pyuca -RedirectTemp /4-19 http://www.unicode.org/Public/UCA/6.3.0/allkeys.txt -RedirectTemp /4-20 https://pypi.org/project/PyICU/ -RedirectTemp /4-21 https://docs.python.org/3.10/library/stdtypes.html#str.isalpha -RedirectTemp /4-22 https://en.wikipedia.org/wiki/Unicode_character_property#General_Category -RedirectTemp /4-23 https://en.wikipedia.org/wiki/Unicode_character_property -RedirectTemp /4-24 https://github.com/microsoft/terminal -RedirectTemp /4-25 https://docs.python.org/3/library/unicodedata.html -RedirectTemp /4-26 https://docs.python.org/3/reference/lexical_analysis.html#string-literal-concatenation -RedirectTemp /4-27 https://docs.python.org/3/library/re.html -RedirectTemp /4-28 https://nedbatchelder.com/text/unipain.html -RedirectTemp /4-29 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 -RedirectTemp /4-30 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ -RedirectTemp /4-31 https://regebro.wordpress.com/2011/03/23/unconfusing-unicode-what-is-unicode/ -RedirectTemp /4-32 https://docs.python.org/3/howto/unicode.html -RedirectTemp /4-33 https://diveintopython3.net/strings.html -RedirectTemp /4-34 https://diveintopython3.net/ -RedirectTemp /4-35 https://finderiko.com/python-book -RedirectTemp /4-36 https://docs.python.org/3.0/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit -RedirectTemp /4-37 https://lucumr.pocoo.org/2013/7/2/the-updated-guide-to-unicode/ -RedirectTemp /4-38 http://python-notes.curiousefficiency.org/en/latest/python3/binary_protocols.html -RedirectTemp /4-39 http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html -RedirectTemp /4-40 https://docs.python.org/3/library/codecs.html#standard-encodings -RedirectTemp /4-41 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Tools/unicode/listcodecs.py -RedirectTemp /4-42 https://www.oreilly.com/library/view/unicode-explained/059610121X/ -RedirectTemp /4-43 https://www.informit.com/store/unicode-demystified-a-practical-programmers-guide-to-9780201700527 -RedirectTemp /4-44 https://unicodebook.readthedocs.io/index.html -RedirectTemp /4-45 https://www.w3.org/International/wiki/Case_folding -RedirectTemp /4-46 http://www.w3.org/TR/charmod-norm/ -RedirectTemp /4-47 http://unicode.org/reports/tr15/ -RedirectTemp /4-48 http://www.unicode.org/faq/normalization.html -RedirectTemp /4-49 http://www.unicode.org/ -RedirectTemp /4-50 http://www.macchiato.com/unicode/nfc-faq -RedirectTemp /4-51 https://stories.moma.org/the-original-emoji-set-has-been-added-to-the-museum-of-modern-arts-collection-c6060e141f61?gi=c403f5a840a5 -RedirectTemp /4-52 https://emojipedia.org/ -RedirectTemp /4-53 https://blog.emojipedia.org/correcting-the-record-on-the-first-emoji-set/ -RedirectTemp /4-54 http://emojitracker.com/ -RedirectTemp /4-55 http://www.unicode.org/glossary/#plain_text -RedirectTemp /4-56 http://www.methods.co.nz/asciidoc/ -RedirectTemp /4-57 https://atlas.oreilly.com/ -############################################################ 05 -RedirectTemp /5-1 https://docs.python.org/3/library/typing.html#typing.TypedDict -RedirectTemp /5-2 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations -RedirectTemp /5-3 https://docs.python.org/3/library/typing.html#typing.get_type_hints -RedirectTemp /5-4 https://docs.python.org/3.8/library/collections.html#collections.somenamedtuple._asdict -RedirectTemp /5-5 https://www.jetbrains.com/pycharm/ -RedirectTemp /5-6 https://www.python.org/dev/peps/pep-0484/#acceptable-type-hints -RedirectTemp /5-7 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass -RedirectTemp /5-8 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass -RedirectTemp /5-9 https://docs.python.org/3/library/dataclasses.html -RedirectTemp /5-10 https://docs.python.org/3/library/dataclasses.html#inheritance -RedirectTemp /5-11 https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations -RedirectTemp /5-12 https://dublincore.org/specifications/dublin-core/ -RedirectTemp /5-13 https://en.wikipedia.org/wiki/Dublin_Core -RedirectTemp /5-14 https://martinfowler.com/bliki/CodeSmell.html -RedirectTemp /5-15 https://martinfowler.com/books/refactoring.html -RedirectTemp /5-16 https://www.python.org/dev/peps/pep-0634/#class-patterns -RedirectTemp /5-17 https://docs.python.org/3/library/dataclasses.html -RedirectTemp /5-18 https://www.python.org/dev/peps/pep-0557/#id47 -RedirectTemp /5-19 https://www.python.org/dev/peps/pep-0557/#id48 -RedirectTemp /5-20 https://www.python.org/dev/peps/pep-0557/#id33 -RedirectTemp /5-21 https://realpython.com -RedirectTemp /5-22 https://realpython.com/python-data-classes/ -RedirectTemp /5-23 https://www.youtube.com/watch?v=T-TwcmT6Rcw -RedirectTemp /5-24 https://www.attrs.org/en/stable/ -RedirectTemp /5-25 https://glyph.twistedmatrix.com/2016/08/attrs.html -RedirectTemp /5-26 https://www.attrs.org/en/stable/why.html -RedirectTemp /5-27 https://github.com/dabeaz/cluegen -RedirectTemp /5-28 https://refactoring.guru/ -RedirectTemp /5-29 https://refactoring.guru/smells/data-class -RedirectTemp /5-30 https://web.archive.org/web/20190204130328/http://catb.org/esr/jargon/html/G/Guido.html -RedirectTemp /5-31 https://web.archive.org/web/20190211161610/http://catb.org/esr/jargon/html/index.html -RedirectTemp /5-32 https://www.attrs.org/en/stable/ -############################################################ 06 -RedirectTemp /6-1 https://www.olin.edu/faculty/profile/lynn-andrea-stein/ -RedirectTemp /6-2 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types -RedirectTemp /6-3 https://pythontutor.com/ -RedirectTemp /6-4 https://docs.python.org/3/library/copy.html -RedirectTemp /6-5 https://en.wikipedia.org/wiki/Principle_of_least_astonishment -RedirectTemp /6-6 https://docs.python.org/3/reference/datamodel.html#object.%5C_%5C_del__ -RedirectTemp /6-7 https://emptysqua.re/blog/pypy-garbage-collection-and-a-deadlock/ -RedirectTemp /6-8 https://www.youtube.com/watch?v=HHFCFJSPWrI&feature=youtu.be -RedirectTemp /6-9 http://pymotw.com/3/copy/ -RedirectTemp /6-10 http://pymotw.com/3/weakref/ -RedirectTemp /6-11 https://docs.python.org/3/library/gc.html -RedirectTemp /6-12 https://devguide.python.org/garbage_collector/ -RedirectTemp /6-13 https://devguide.python.org/ -RedirectTemp /6-14 https://www.python.org/dev/peps/pep-0442/ -RedirectTemp /6-15 https://en.wikipedia.org/wiki/String_interning -RedirectTemp /6-16 https://en.wikipedia.org/wiki/Haddocks%27_Eyes -RedirectTemp /6-17 https://thp.io/2012/python-gc/python_gc_final_2012-01-22.pdf -############################################################ 07 -RedirectTemp /7-1 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html -RedirectTemp /7-2 https://www.fluentpython.com/extra/function-introspection/ -RedirectTemp /7-3 https://docs.python.org/3/library/functions.html#map -RedirectTemp /7-4 https://en.wikipedia.org/wiki/Functional_programming -RedirectTemp /7-5 https://docs.python.org/3/howto/functional.html -RedirectTemp /7-6 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy -RedirectTemp /7-7 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters -RedirectTemp /7-8 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters -RedirectTemp /7-9 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/functools.py#L341 -RedirectTemp /7-10 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy -RedirectTemp /7-11 https://docs.python.org/3/howto/functional.html -RedirectTemp /7-12 https://stackoverflow.com/questions/3252228/python-why-is-functools-partial-necessary -RedirectTemp /7-13 https://speakerdeck.com/ramalho/beyond-paradigms-berlin-edition -RedirectTemp /7-14 https://www.youtube.com/watch?v=bF3a2VYXxa0 -RedirectTemp /7-15 http://cs.brown.edu/~sk/Publications/Papers/Published/sk-teach-pl-post-linnaean/ -RedirectTemp /7-16 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html -RedirectTemp /7-17 https://raw.githubusercontent.com/python/cpython/main/Misc/HISTORY -RedirectTemp /7-18 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html -############################################################ 08 -RedirectTemp /8-1 https://www.python.org/dev/peps/pep-0484/#non-goals -RedirectTemp /8-2 https://github.com/python/typing/issues/182 -RedirectTemp /8-3 https://github.com/python/mypy/issues/731 -RedirectTemp /8-4 https://github.com/google/pytype -RedirectTemp /8-5 https://github.com/Microsoft/pyright -RedirectTemp /8-6 https://pyre-check.org/ -RedirectTemp /8-7 https://mypy.readthedocs.io/en/stable/introduction.html -RedirectTemp /8-8 https://mypy.readthedocs.io/en/stable/config_file.html -RedirectTemp /8-9 https://pypi.org/project/flake8/ -RedirectTemp /8-10 https://pypi.org/project/blue/ -RedirectTemp /8-11 https://pypi.org/project/black/ -RedirectTemp /8-12 https://wefearchange.org/2020/11/steeringcouncil.rst.html -RedirectTemp /8-13 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes -RedirectTemp /8-14 https://en.wikipedia.org/wiki/Barbara_Liskov -RedirectTemp /8-15 https://en.wikipedia.org/wiki/Behavioral_subtyping -RedirectTemp /8-16 https://www.python.org/dev/peps/pep-0585/#implementation -RedirectTemp /8-17 https://docs.python.org/3/library/typing.html#module-contents -RedirectTemp /8-18 https://en.wikipedia.org/wiki/Geohash -RedirectTemp /8-19 https://en.wikipedia.org/wiki/Inverted_index -RedirectTemp /8-20 https://docs.python.org/3/library/typing.html#typing.List -RedirectTemp /8-21 https://docs.python.org/3/library/typing.html#typing.Dict -RedirectTemp /8-22 https://docs.python.org/3/library/typing.html#typing.Set -RedirectTemp /8-23 https://www.python.org/dev/peps/pep-0585/#implementation -RedirectTemp /8-24 https://docs.python.org/3/library/numbers.html -RedirectTemp /8-25 https://docs.python.org/3/library/typing.html#typing.List -RedirectTemp /8-26 https://github.com/python/typeshed -RedirectTemp /8-27 https://github.com/python/typeshed/blob/66cd36268a6a667714efaa27198a41d0d7f89477/stdlib/2and3/math.pyi#L45 -RedirectTemp /8-28 https://docs.python.org/3/library/statistics.html#statistics.mode -RedirectTemp /8-29 https://github.com/python/cpython/blob/822efa5695b5ba6c2316c1400e4e9ec2546f7ea5/Lib/statistics.py#L534 -RedirectTemp /8-30 https://github.com/python/typeshed/blob/e1e99245bb46223928eba68d4fc74962240ba5b4/stdlib/3/statistics.pyi -RedirectTemp /8-31 https://docs.python.org/3/library/statistics.html#statistics.mode -RedirectTemp /8-32 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/3/statistics.pyi#L32 -RedirectTemp /8-33 https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ -RedirectTemp /8-34 https://docs.python.org/3/library/typing.html#typing.Callable -RedirectTemp /8-35 https://pypi.org/project/blue/ -RedirectTemp /8-36 https://www.python.org/dev/peps/pep-0484/#id38 -RedirectTemp /8-37 https://docs.google.com/document/d/1aXs1tpwzPjW9MdsG5dI7clNFyYayFBkcXwRDo-qvbIk/preview -RedirectTemp /8-38 https://www.oreilly.com/library/view/the-best-software/9781590595008/ -RedirectTemp /8-39 https://www.youtube.com/watch?v=YFexUDjHO6w -RedirectTemp /8-40 https://www.youtube.com/watch?v=YFexUDjHO6w&t=13m40s -RedirectTemp /8-41 https://bernat.tech/posts/the-state-of-type-hints-in-python/ -RedirectTemp /8-42 https://realpython.com/python-type-checking/ -RedirectTemp /8-43 https://cjolowicz.github.io/posts/hypermodern-python-04-typing/ -RedirectTemp /8-44 https://mypy.readthedocs.io/en/stable/index.html -RedirectTemp /8-45 https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html -RedirectTemp /8-46 https://mypy.readthedocs.io/en/stable/common_issues.html -RedirectTemp /8-47 https://github.com/typeddjango/awesome-python-typing -RedirectTemp /8-48 https://docs.python.org/3/library/functions.html#max -RedirectTemp /8-49 https://en.wikipedia.org/wiki/Linguistic_relativity -RedirectTemp /8-50 https://pypistats.org/top -RedirectTemp /8-51 https://github.com/psf/requests/issues/3855 -RedirectTemp /8-52 https://lwn.net/Articles/643399/ -RedirectTemp /8-53 https://docs.python-requests.org/en/master/api/#requests.request -RedirectTemp /8-54 https://queue.acm.org/detail.cfm?id=1039523 -############################################################ 09 -RedirectTemp /9-1 https://docs.python.org/3/library/dis.html -RedirectTemp /9-2 https://en.wikipedia.org/wiki/Memoization -RedirectTemp /9-3 https://numpy.org/doc/stable/user/basics.types.html -RedirectTemp /9-4 https://docs.python.org/3/library/functools.html#functools.singledispatch -RedirectTemp /9-5 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/README.md -RedirectTemp /9-6 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/01-how-you-implemented-your-python-decorator-is-wrong.md -RedirectTemp /9-7 https://wrapt.readthedocs.io/en/latest/ -RedirectTemp /9-8 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch09.html -RedirectTemp /9-9 https://pypi.org/project/decorator/ -RedirectTemp /9-10 https://wiki.python.org/moin/PythonDecoratorLibrary -RedirectTemp /9-11 http://web.archive.org/web/20201109032203/http://effbot.org/zone/closure.htm -RedirectTemp /9-12 https://www.python.org/dev/peps/pep-3104/ -RedirectTemp /9-13 https://www.python.org/dev/peps/pep-0227/ -RedirectTemp /9-14 https://www.python.org/dev/peps/pep-0443/ -RedirectTemp /9-15 https://www.artima.com/weblogs/viewpost.jsp?thread=101605 -RedirectTemp /9-16 https://reg.readthedocs.io/en/latest/ -RedirectTemp /9-17 https://morepath.readthedocs.io/en/latest/ -RedirectTemp /9-18 https://www.gnu.org/software/emacs/manual/html_node/elisp/Dynamic-Binding.html -RedirectTemp /9-19 http://www.paulgraham.com/rootsoflisp.html -RedirectTemp /9-20 http://www-formal.stanford.edu/jmc/recursive/recursive.html -RedirectTemp /9-21 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this -############################################################ 10 -RedirectTemp /10-1 https://en.wikipedia.org/wiki/Software_design_pattern -RedirectTemp /10-2 https://en.wikipedia.org/wiki/Iterator_pattern -RedirectTemp /10-3 https://github.com/python/mypy/issues/9397 -RedirectTemp /10-4 http://www.norvig.com/design-patterns/index.htm -RedirectTemp /10-5 https://pyvideo.org/video/1110/python-design-patterns/ -RedirectTemp /10-6 http://www.aleax.it/gdd_pydp.pdf -RedirectTemp /10-7 https://perl.plover.com/yak/design/ -RedirectTemp /10-8 https://en.wikipedia.org/wiki/Turtles_all_the_way_down -############################################################ 11 -RedirectTemp /11-1 https://blog.startifact.com/posts/older/what-is-pythonic.html -RedirectTemp /11-2 https://julien.danjou.info/guide-python-static-class-abstract-methods/ -RedirectTemp /11-3 https://docs.python.org/3/library/string.html#formatspec -RedirectTemp /11-4 https://docs.python.org/3/reference/lexical_analysis.html#f-strings -RedirectTemp /11-5 https://docs.python.org/3/library/string.html#format-string-syntax -RedirectTemp /11-6 https://docs.python.org/3/library/string.html#formatspec -RedirectTemp /11-7 https://docs.python.org/3/reference/datamodel.html#object.__hash__ -RedirectTemp /11-8 https://web.archive.org/web/20161025185040/http://pythonpaste.org/StyleGuide.html -RedirectTemp /11-9 https://docs.python.org/3/tutorial/modules.html#more-on-modules -RedirectTemp /11-10 https://docs.python.org/3/library/gettext.html#gettext.NullTranslations -RedirectTemp /11-11 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/mem_test.py -RedirectTemp /11-12 https://docs.python.org/3/reference/datamodel.html#basic-customization -RedirectTemp /11-13 http://esug.org/data/HistoricalDocuments/TheSmalltalkReport/ST07/04wo.pdf -RedirectTemp /11-14 https://www.artima.com/articles/the-simplest-thing-that-could-possibly-work#part3 -RedirectTemp /11-15 https://docs.oracle.com/javase/tutorial/essential/environment/security.html -RedirectTemp /11-16 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/private/Expose.java -RedirectTemp /11-17 https://docs.oracle.com/javase/tutorial/essential/environment/security.html -############################################################ 12 -RedirectTemp /12-1 https://en.wikipedia.org/wiki/Vector_space_model -RedirectTemp /12-2 https://pypi.org/project/gensim/ -RedirectTemp /12-3 https://docs.python.org/3/library/functions.html#enumerate -RedirectTemp /12-4 https://mathworld.wolfram.com/Hypersphere.html -RedirectTemp /12-5 https://en.wikipedia.org/wiki/Fold_(higher-order_function) -RedirectTemp /12-6 https://docs.python.org/2.5/whatsnew/pep-357.html -RedirectTemp /12-7 https://docs.python.org/3/reference/datamodel.html#special-method-names -RedirectTemp /12-8 https://en.wikipedia.org/wiki/KISS_principle -RedirectTemp /12-9 https://mail.python.org/pipermail/python-list/2000-July/046184.html -RedirectTemp /12-10 https://en.wikipedia.org/wiki/Duck_typing -RedirectTemp /12-11 https://mail.python.org/mailman/listinfo/python-list -RedirectTemp /12-12 https://mail.python.org/pipermail/python-list/2003-April/218568.html -############################################################ 13 -RedirectTemp /13-1 https://docs.python.org/3/c-api/index.html -RedirectTemp /13-2 https://docs.python.org/3/c-api/sequence.html -RedirectTemp /13-3 https://github.com/python/cpython/blob/31ceccb2c77854893f3a754aca04bedd74bedb10/Lib/_collections_abc.py#L870 -RedirectTemp /13-4 https://en.wikipedia.org/wiki/Monkey_patch -RedirectTemp /13-5 https://www.gevent.org/api/gevent.monkey.html -RedirectTemp /13-6 https://docs.python.org/3/library/random.html#random.shuffle -RedirectTemp /13-7 https://docs.python.org/3/reference/datamodel.html#emulating-container-types -RedirectTemp /13-8 https://docs.python.org/3/library/collections.html#collections.namedtuple -RedirectTemp /13-9 https://github.com/python/typeshed/blob/24afb531ffd07083d6a74be917342195062f7277/stdlib/collections/__init__.pyi -RedirectTemp /13-10 https://docs.python.org/3/glossary.html#term-abstract-base-class -RedirectTemp /13-11 https://en.wikipedia.org/wiki/Duck_typing#History -RedirectTemp /13-12 http://ptgmedia.pearsoncmg.com/images/020163371x/items/item33.html -RedirectTemp /13-13 https://docs.python.org/3/library/bisect.html#bisect.bisect -RedirectTemp /13-14 https://github.com/python/cpython/blob/main/Lib/_collections_abc.py -RedirectTemp /13-15 https://github.com/python/cpython/blob/main/Lib/abc.py -RedirectTemp /13-16 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes -RedirectTemp /13-17 https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable -RedirectTemp /13-18 https://docs.python.org/3/library/abc.html -RedirectTemp /13-19 https://docs.python.org/dev/library/abc.html#abc.abstractmethod -RedirectTemp /13-20 https://docs.python.org/dev/library/abc.html -RedirectTemp /13-21 https://docs.python.org/3/library/os.html#os.urandom -RedirectTemp /13-22 https://github.com/python/mypy/issues/2922 -RedirectTemp /13-23 https://docs.python.org/3/library/stdtypes.html#truth -RedirectTemp /13-24 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py -RedirectTemp /13-25 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L369 -RedirectTemp /13-26 https://github.com/python/cpython/blob/c0a9afe2ac1820409e6173bd1893ebee2cf50270/Lib/abc.py#L196 -RedirectTemp /13-27 https://bugs.python.org/issue31333 -RedirectTemp /13-28 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Modules/_abc.c#L605 -RedirectTemp /13-29 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L881 -RedirectTemp /13-30 https://docs.python.org/3/library/typing.html#protocols -RedirectTemp /13-31 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Lib/typing.py#L1751 -RedirectTemp /13-32 https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ -RedirectTemp /13-33 https://martinfowler.com/bliki/RoleInterface.html -RedirectTemp /13-34 https://en.wikipedia.org/wiki/Interface_segregation_principle -RedirectTemp /13-35 https://github.com/python/typeshed/blob/master/CONTRIBUTING.md -RedirectTemp /13-36 https://gist.github.com/asukakenji/ac8a05644a2e98f1d5ea8c299541fce9 -RedirectTemp /13-37 https://www.python.org/dev/peps/pep-0544/#runtime-checkable-decorator-and-narrowing-types-by-isinstance -RedirectTemp /13-38 https://www.python.org/dev/peps/pep-0544/#merging-and-extending-protocols -RedirectTemp /13-39 https://numpy.org/devdocs/user/basics.types.html -RedirectTemp /13-40 https://github.com/python/typeshed/blob/master/stdlib/statistics.pyi -RedirectTemp /13-41 https://bugs.python.org/issue41974 -RedirectTemp /13-42 https://glyph.twistedmatrix.com/2020/07/new-duck.html -RedirectTemp /13-43 https://glyph.twistedmatrix.com/2021/03/interfaces-and-protocols.html -RedirectTemp /13-44 https://plone.org/ -RedirectTemp /13-45 https://trypyramid.com/ -RedirectTemp /13-46 https://twistedmatrix.com/trac/ -RedirectTemp /13-47 https://www.artima.com/articles/contracts-in-python -RedirectTemp /13-48 https://martinfowler.com/bliki/DynamicTyping.html -RedirectTemp /13-49 https://martinfowler.com/bliki/RoleInterface.html -RedirectTemp /13-50 https://mypy.readthedocs.io/en/stable/protocols.html -RedirectTemp /13-51 https://pymotw.com/3/abc/index.html -RedirectTemp /13-52 https://www.python.org/dev/peps/pep-3119/ -RedirectTemp /13-53 https://www.python.org/dev/peps/pep-3141/ -RedirectTemp /13-54 https://docs.python.org/3/library/numbers.html -RedirectTemp /13-55 https://github.com/python/mypy/issues/3186 -RedirectTemp /13-56 https://stackoverflow.com/questions/69334475/how-to-hint-at-number-types-i-e-subclasses-of-number-not-numbers-themselv/69383462#69383462 -RedirectTemp /13-57 https://github.com/python/mypy/issues/3186 -RedirectTemp /13-58 https://martinfowler.com/articles/lean-inception/ -RedirectTemp /13-59 https://martinfowler.com -RedirectTemp /13-60 https://www.jetbrains.com/pycharm/ -RedirectTemp /13-61 https://wingware.com/ -RedirectTemp /13-62 https://code.visualstudio.com/ -############################################################ 14 -RedirectTemp /14-1 http://worrydream.com/EarlyHistoryOfSmalltalk/ -RedirectTemp /14-2 https://docs.python.org/3/tutorial/classes.html -RedirectTemp /14-3 https://docs.python.org/3/library/collections.html#ordereddict-examples-and-recipes -RedirectTemp /14-4 https://discuss.python.org/t/is-it-time-to-deprecate-unbound-super-methods/1833 -RedirectTemp /14-5 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types -RedirectTemp /14-6 https://docs.python.org/3/library/collections.html -RedirectTemp /14-7 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/strkeydict_dictsub.py -RedirectTemp /14-8 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types -RedirectTemp /14-9 https://en.wikipedia.org/wiki/Breadth-first_search -RedirectTemp /14-10 https://www.python.org/download/releases/2.3/mro/ -RedirectTemp /14-11 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/uppermixin.py -RedirectTemp /14-12 https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html -RedirectTemp /14-13 https://docs.python.org/3/library/collections.abc.html -RedirectTemp /14-14 https://github.com/python/cpython/blob/8ece98a7e418c3c68a4c61bc47a2d0931b59a889/Lib/collections/__init__.py#L1084 -RedirectTemp /14-15 https://docs.python.org/3/library/http.server.html -RedirectTemp /14-16 https://github.com/python/cpython/blob/17c23167942498296f0bdfffe52e72d53d66d693/Lib/http/server.py#L144 -RedirectTemp /14-17 https://github.com/python/cpython/blob/699ee016af5736ffc80f68359617611a22b72943/Lib/socketserver.py#L664 -RedirectTemp /14-18 https://docs.python.org/3/library/socketserver.html#socketserver.ForkingMixIn -RedirectTemp /14-19 https://docs.python.org/3/library/os.html#os.fork -RedirectTemp /14-20 https://en.wikipedia.org/wiki/POSIX -RedirectTemp /14-21 http://ccbv.co.uk/ -RedirectTemp /14-22 https://github.com/django/django/tree/main/django/views/generic -RedirectTemp /14-23 https://en.wikipedia.org/wiki/Template_method_pattern -RedirectTemp /14-24 https://docs.python.org/3/library/tkinter.html -RedirectTemp /14-25 https://docs.python.org/3/library/tkinter.ttk.html -RedirectTemp /14-26 https://docs.oracle.com/javase/10/docs/api/java/awt/package-tree.html -RedirectTemp /14-27 https://docs.oracle.com/javase/10/docs/api/javax/swing/package-tree.html -RedirectTemp /14-28 https://squeak.org/ -RedirectTemp /14-29 https://github.com/django/django/blob/b64db05b9cedd96905d637a2d824cbbf428e40e7/django/views/generic/list.py#L194 -RedirectTemp /14-30 https://github.com/python/cpython/blob/8ed183391241f0c73e7ba7f42b1d49fc02985f7b/Lib/tkinter/__init__.py#L2618 -RedirectTemp /14-31 https://docs.python.org/3/library/socketserver.html -RedirectTemp /14-32 https://docs.python.org/3/library/socketserver.html#socketserver.BaseServer -RedirectTemp /14-33 https://github.com/python/cpython/blob/699ee016af5736ffc80f68359617611a22b72943/Lib/socketserver.py#L153 -RedirectTemp /14-34 https://docs.python.org/3/library/typing.html#typing.final -RedirectTemp /14-35 https://docs.python.org/3/library/typing.html#typing.Final -RedirectTemp /14-36 https://docs.python.org/3/library/collections.abc.html -RedirectTemp /14-37 https://hynek.me/articles/python-subclassing-redux/ -RedirectTemp /14-38 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch08.html#super -RedirectTemp /14-39 https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ -RedirectTemp /14-40 https://fuhm.net/super-harmful/ -RedirectTemp /14-41 https://stackoverflow.com/questions/30190185/how-to-use-super-with-one-argument/30190341#30190341 -RedirectTemp /14-42 https://www.artima.com/weblogs/viewpost.jsp?thread=246488 -RedirectTemp /14-43 https://www.artima.com/weblogs/viewpost.jsp?thread=281127 -RedirectTemp /14-44 https://www.artima.com/weblogs/viewpost.jsp?thread=246341 -RedirectTemp /14-45 https://www.artima.com/weblogs/viewpost.jsp?thread=246483 -RedirectTemp /14-46 https://www.artima.com/weblogs/viewpost.jsp?thread=236275 -RedirectTemp /14-47 https://www.artima.com/weblogs/viewpost.jsp?thread=236278 -RedirectTemp /14-48 https://www.artima.com/weblogs/viewpost.jsp?thread=237121 -RedirectTemp /14-49 https://python-patterns.guide/gang-of-four/composition-over-inheritance/ -RedirectTemp /14-50 https://python-patterns.guide/ -RedirectTemp /14-51 https://www.youtube.com/watch?v=3MNVP9-hglc -RedirectTemp /14-52 http://worrydream.com/EarlyHistoryOfSmalltalk/ -RedirectTemp /14-53 https://en.wikipedia.org/wiki/Polymorphism_(computer_science) -############################################################ 15 -RedirectTemp /15-1 https://www.youtube.com/watch?v=csL8DLXGNlU&t=92m5s -RedirectTemp /15-2 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/2and3/builtins.pyi#L1434 -RedirectTemp /15-3 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/2and3/builtins.pyi -RedirectTemp /15-4 https://twitter.com/gwidion/status/1265384692464967680 -RedirectTemp /15-5 https://pypi.org/project/pydantic/ -RedirectTemp /15-6 https://google.github.io/pytype/faq.html -RedirectTemp /15-7 https://google.github.io/pytype/faq.html -RedirectTemp /15-8 https://lxml.de/ -RedirectTemp /15-9 https://docs.python.org/3/library/xml.etree.elementtree.html -RedirectTemp /15-10 https://mypy.readthedocs.io/en/stable/common_issues.html -RedirectTemp /15-11 https://mypy.readthedocs.io/en/stable/common_issues.html#types-of-empty-collections -RedirectTemp /15-12 https://github.com/python/typing/issues/182 -RedirectTemp /15-13 https://pypi.org/project/pydantic/ -RedirectTemp /15-14 https://mypy.readthedocs.io/en/stable/type_narrowing.html#casts -RedirectTemp /15-15 https://github.com/python/cpython/blob/bee66d3cb98e740f9d8057eb7f503122052ca5d8/Lib/typing.py#L1340 -RedirectTemp /15-16 https://www.python.org/dev/peps/pep-0484/#casts -RedirectTemp /15-17 https://github.com/python/typeshed/issues/5535 -RedirectTemp /15-18 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams -RedirectTemp /15-19 https://github.com/python/cpython/blob/b798ab06937f8bb24b444a49dd42e11fff15e654/Lib/test/test_asyncio/test_server.py#L55 -RedirectTemp /15-20 https://en.wikipedia.org/wiki/Code_smell -RedirectTemp /15-21 https://mail.python.org/archives/list/typing-sig@python.org/message/5LCWMN2UY2UQNLC5Z47GHBZKSPZW4I63/ -RedirectTemp /15-22 https://mypy.readthedocs.io/en/stable/error_codes.html#error-codes -RedirectTemp /15-23 https://github.com/fluentpython/example-code-2e/blob/master/15-more-types/clip_annot.py -RedirectTemp /15-24 https://docs.python.org/3/library/typing.html#introspection-helpers -RedirectTemp /15-25 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations -RedirectTemp /15-26 https://www.python.org/dev/peps/pep-0563/#abstract -RedirectTemp /15-27 https://mail.python.org/archives/list/python-dev@python.org/message/ZBJ7MD6CSGM6LZAOTET7GXAVBZB7O77O/ -RedirectTemp /15-28 https://docs.python.org/3.10/howto/annotations.html -RedirectTemp /15-29 https://docs.python.org/3/library/typing.html#user-defined-generic-types -RedirectTemp /15-30 https://github.com/python/typeshed/blob/bfc83c365a0b26ab16586beac77ff16729d0e473/stdlib/builtins.pyi#L743 -RedirectTemp /15-31 https://docs.python.org/3.10/library/typing.html#typing.FrozenSet -RedirectTemp /15-32 https://docs.python.org/3.10/library/typing.html#typing.Generator -RedirectTemp /15-33 https://docs.python.org/3.10/library/typing.html#typing.AsyncGenerator -RedirectTemp /15-34 https://github.com/python/cpython/blob/46b16d0bdbb1722daed10389e27226a2370f1635/Lib/typing.py#L1786 -RedirectTemp /15-35 https://github.com/python/typeshed/blob/2a9f081abbf01134e4e04ced6a750107db904d70/stdlib/builtins.pyi#L239 -RedirectTemp /15-36 https://www.oreilly.com/library/view/robust-python/9781098100650/ -RedirectTemp /15-37 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance -RedirectTemp /15-38 https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generic-types -RedirectTemp /15-39 https://mypy.readthedocs.io/en/stable/common_issues.html#variance -RedirectTemp /15-40 https://www.artima.com/weblogs/viewpost.jsp?thread=85551 -RedirectTemp /15-41 https://dl.acm.org/action/cookieAbsent -RedirectTemp /15-42 http://bracha.org/pluggableTypesPosition.pdf -RedirectTemp /15-43 https://www.researchgate.net/publication/213886116_Static_Typing_Where_Possible_Dynamic_Typing_When_Needed_The_End_of_the_Cold_War_Between_Programming_Languages -RedirectTemp /15-44 https://www.atomickotlin.com/atomickotlin/ -RedirectTemp /15-45 https://www.informit.com/store/effective-java-9780134685991 -RedirectTemp /15-46 https://www.manning.com/books/programming-with-types -RedirectTemp /15-47 https://www.oreilly.com/library/view/programming-typescript/9781492037644/ -RedirectTemp /15-48 https://www.informit.com/store/dart-programming-language-9780321927705 -RedirectTemp /15-49 https://www.yodaiken.com/2017/09/15/bad-ideas-in-type-theory/ -RedirectTemp /15-50 https://www.yodaiken.com/2017/11/30/types-considered-harmful-ii/ -RedirectTemp /15-51 https://web.archive.org/web/20071010002142/http://weblogs.java.net/blog/arnold/archive/2005/06/generics_consid_1.html -RedirectTemp /15-52 https://github.com/python/cpython/blob/3e7ee02327db13e4337374597cdc4458ecb9e3ad/Lib/asyncio/trsock.py#L5 -RedirectTemp /15-53 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance -############################################################ 16 -RedirectTemp /16-1 http://www.gotw.ca/publications/c_family_interview.htm -RedirectTemp /16-2 https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations -RedirectTemp /16-3 https://docs.python.org/3/reference/datamodel.html#object.__neg__ -RedirectTemp /16-4 https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#boolean-indexing -RedirectTemp /16-5 https://docs.python.org/3/library/collections.html#collections.Counter -RedirectTemp /16-6 https://docs.python.org/3/reference/datamodel.html#emulating-container-types -RedirectTemp /16-7 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations -RedirectTemp /16-8 https://www.fluentpython.com/lingo/#fail-fast -RedirectTemp /16-9 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Objects/typeobject.c#L4598 -RedirectTemp /16-10 https://neopythonic.blogspot.com/2019/03/why-operators-are-useful.html -RedirectTemp /16-11 https://treyhunner.com/2019/03/python-deep-comparisons-and-code-readability/ -RedirectTemp /16-12 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations -RedirectTemp /16-13 https://docs.python.org/3/library/pathlib.html -RedirectTemp /16-14 https://pypi.org/project/scapy/ -RedirectTemp /16-15 https://scapy.readthedocs.io/en/latest/usage.html#stacking-layers -RedirectTemp /16-16 https://docs.python.org/3/library/functools.html#functools.total_ordering -RedirectTemp /16-17 https://wiki.illinois.edu//wiki/download/attachments/273416327/ingalls.pdf -RedirectTemp /16-18 https://wiki.illinois.edu//wiki/download/attachments/273416327/double-dispatch.pdf -RedirectTemp /16-19 http://www.gotw.ca/publications/c_family_interview.htm -RedirectTemp /16-20 http://www.gotw.ca/publications/c_family_interview.htm -RedirectTemp /16-21 https://doc.rust-lang.org/std/ops/index.html -RedirectTemp /16-22 https://www.fluentpython.com/lingo/#lazy -############################################################ 17 -RedirectTemp /17-1 http://www.paulgraham.com/icad.html -RedirectTemp /17-2 https://en.wikipedia.org/wiki/Sentinel_value -RedirectTemp /17-3 https://docs.python.org/3.10/library/functions.html#iter -RedirectTemp /17-4 https://docs.python.org/3.10/library/functions.html#iter -RedirectTemp /17-5 https://github.com/python/cpython/blob/b1930bf75f276cd7ca08c4455298128d89adf7d1/Lib/_collections_abc.py#L271 -RedirectTemp /17-6 https://github.com/python/cpython/blob/main/Lib/types.py#L6 -RedirectTemp /17-7 https://en.wikipedia.org/wiki/CLU_(programming_language) -RedirectTemp /17-8 https://docs.python.org/3/glossary.html -RedirectTemp /17-9 https://docs.python.org/3/glossary.html#term-generator-iterator -RedirectTemp /17-10 https://docs.python.org/3/glossary.html#term-generator-expression -RedirectTemp /17-11 https://marc.info/?l=python-list&m=141826925106951&w=2 -RedirectTemp /17-12 https://docs.python.org/3/library/os.html#os.walk -RedirectTemp /17-13 https://docs.python.org/3/library/itertools.html -RedirectTemp /17-14 https://docs.python.org/3/library/exceptions.html#exception-hierarchy -RedirectTemp /17-15 https://en.wikipedia.org/wiki/Depth-first_search -RedirectTemp /17-16 https://docs.python.org/3.10/library/typing.html#typing.TypeAlias -RedirectTemp /17-17 https://docs.python.org/3/library/typing.html#typing.Generator -RedirectTemp /17-18 http://www.dabeaz.com/coroutines/Coroutines.pdf -RedirectTemp /17-19 http://www.dabeaz.com/coroutines/Coroutines.pdf -RedirectTemp /17-20 https://mail.python.org/pipermail/python-ideas/2009-April/003841.html -RedirectTemp /17-21 https://mail.python.org/pipermail/python-ideas/2009-April/003912.html -RedirectTemp /17-22 https://docs.python.org/3/library/exceptions.html#StopIteration -RedirectTemp /17-23 https://docs.python.org/3/reference/expressions.html#yield-expressions -RedirectTemp /17-24 https://docs.python.org/3/reference/index.html -RedirectTemp /17-25 https://github.com/python/cpython/blob/6f743e7a4da904f61dfa84cc7d7385e4dcc79ac5/Lib/typing.py#L2060 -RedirectTemp /17-26 http://catb.org/~esr/jargon/html/G/grok.html -RedirectTemp /17-27 https://docs.python.org/3/reference/expressions.html#yieldexpr -RedirectTemp /17-28 https://docs.python.org/3/library/itertools.html -RedirectTemp /17-29 https://docs.python.org/3/library/itertools.html#itertools-recipes -RedirectTemp /17-30 https://more-itertools.readthedocs.io/en/stable/index.html -RedirectTemp /17-31 https://rittau.org/2006/11/java-iterators-are-not-iterable/ -RedirectTemp /17-32 https://docs.python.org/3/whatsnew/3.3.html#pep-380-syntax-for-delegating-to-a-subgenerator -RedirectTemp /17-33 http://www.dabeaz.com/generators/ -RedirectTemp /17-34 http://www.dabeaz.com/coroutines/ -RedirectTemp /17-35 https://archive.org/details/pyvideo_213___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-1-of-3 -RedirectTemp /17-36 https://archive.org/details/pyvideo_215___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-2-of-3 -RedirectTemp /17-37 https://archive.org/details/pyvideo_214___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-3-of-3 -RedirectTemp /17-38 http://www.dabeaz.com/finalgenerator/ -RedirectTemp /17-39 https://web.archive.org/web/20200218150637/http://seriously.dontusethiscode.com/2013/05/01/greedy-coroutine.html -RedirectTemp /17-40 https://effectivepython.com/ -RedirectTemp /17-41 https://effectivepython.com/2015/03/10/consider-coroutines-to-run-many-functions-concurrently -RedirectTemp /17-42 https://en.wikipedia.org/wiki/Conway's_Game_of_Life -RedirectTemp /17-43 https://gist.github.com/ramalho/da5590bc38c973408839 -RedirectTemp /17-44 https://gist.github.com/ramalho/da5590bc38c973408839 -RedirectTemp /17-45 https://journal.code4lib.org/articles/4893 -RedirectTemp /17-46 https://github.com/fluentpython/isis2json -RedirectTemp /17-47 https://github.com/fluentpython/isis2json/blob/master/README.rst -############################################################ 18 -RedirectTemp /18-1 https://pyvideo.org/video/1669/keynote-3/ -RedirectTemp /18-2 https://docs.python.org/3/library/sqlite3.html#using-the-connection-as-a-context-manager -RedirectTemp /18-3 https://docs.python.org/3/library/threading.html#using-locks-conditions-and-semaphores-in-the-with-statement -RedirectTemp /18-4 https://docs.python.org/3/library/decimal.html#decimal.localcontext -RedirectTemp /18-5 https://docs.python.org/3/library/unittest.mock.html#patch -RedirectTemp /18-6 https://docs.python.org/3/library/contextlib.html#contextlib.redirect_stdout -RedirectTemp /18-7 https://docs.python.org/3/library/sys.html#sys.exc_info -RedirectTemp /18-8 https://en.wikipedia.org/wiki/LL_parser -RedirectTemp /18-9 https://docs.python.org/3/library/contextlib.html -RedirectTemp /18-10 https://github.com/python/cpython/blob/8afab2ebbc1b343cd88d058914cf622fe687a2be/Lib/contextlib.py#L123 -RedirectTemp /18-11 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ -RedirectTemp /18-12 https://docs.python.org/3/library/fileinput.html#fileinput.input -RedirectTemp /18-13 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ -RedirectTemp /18-14 https://en.wikipedia.org/wiki/Euclidean_algorithm -RedirectTemp /18-15 https://github.com/fluentpython/example-code-2e/tree/master/18-with-match/lispy/py3.10/ -RedirectTemp /18-16 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lispy.py -RedirectTemp /18-17 https://github.com/fluentpython/example-code-2e/blob/6527037ae7319ba370a1ee2d9fe79214d0ed9452/18-with-match/lispy/py3.10/lis.py#L35 -RedirectTemp /18-18 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/examples_test.py -RedirectTemp /18-19 https://github.com/python/typeshed/issues/6042 -RedirectTemp /18-20 https://github.com/fluentpython/lispy/tree/main/mylis -RedirectTemp /18-21 https://github.com/fluentpython/example-code-2e/blob/00e4741926e1b771ee7c753148b1415c0bd12e39/02-array-seq/lispy/py3.10/examples_test.py -RedirectTemp /18-22 https://mitpress.mit.edu/sites/default/files/sicp/index.html -RedirectTemp /18-23 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/examples_test.py -RedirectTemp /18-24 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/lis.py -RedirectTemp /18-25 https://www.python.org/dev/peps/pep-0634/#or-patterns -RedirectTemp /18-26 https://en.wikipedia.org/wiki/Lambda#Character_encodings -RedirectTemp /18-27 https://docs.python.org/3/reference/compound_stmts.html -RedirectTemp /18-28 https://docs.python.org/3/glossary.html#term-eafp -RedirectTemp /18-29 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 -RedirectTemp /18-30 https://docs.python.org/3/reference/compound_stmts.html -RedirectTemp /18-31 https://stackoverflow.com/questions/16138232/is-it-a-good-practice-to-use-try-except-else-in-python -RedirectTemp /18-32 https://docs.python.org/3/library/stdtypes.html#typecontextmanager -RedirectTemp /18-33 https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers -RedirectTemp /18-34 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 -RedirectTemp /18-35 https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger-1?slide=34 -RedirectTemp /18-36 https://preshing.com/20110920/the-python-with-statement-by-example/ -RedirectTemp /18-37 https://www.rath.org/on-the-beauty-of-pythons-exitstack.html -RedirectTemp /18-38 https://norvig.com/lispy.html -RedirectTemp /18-39 https://norvig.com/lispy2.html -RedirectTemp /18-40 https://github.com/norvig/pytudes -RedirectTemp /18-41 https://github.com/fluentpython/lispy -RedirectTemp /18-42 https://racket-lang.org/ -RedirectTemp /18-43 https://pyvideo.org/video/1669/keynote-3/ -RedirectTemp /18-44 https://en.wikipedia.org/wiki/Tail_call -RedirectTemp /18-45 https://2ality.com/2015/06/tail-call-optimization.html -RedirectTemp /18-46 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lis.py -RedirectTemp /18-47 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lispy.py -RedirectTemp /18-48 http://neopythonic.blogspot.com/2009/04/final-words-on-tail-calls.html -RedirectTemp /18-49 https://webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit/ -RedirectTemp /18-50 http://kangax.github.io/compat-table/es6/ -RedirectTemp /18-51 https://world.hey.com/mgmarlow/what-happened-to-proper-tail-calls-in-javascript-5494c256 -RedirectTemp /18-52 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html -RedirectTemp /18-53 https://github.com/fluentpython/lispy/blob/main/mylis/mylis_2/lis.py -RedirectTemp /18-54 https://github.com/fluentpython/lispy/tree/main/mylis -############################################################ 19 -RedirectTemp /19-1 https://go.dev/blog/waza-talk -RedirectTemp /19-2 https://en.wikipedia.org/wiki/Graphics_processing_unit -RedirectTemp /19-3 https://docs.python.org/3/library/sys.html#sys.getswitchinterval -RedirectTemp /19-4 https://docs.python.org/3/library/sys.html#sys.setswitchinterval -RedirectTemp /19-5 https://en.wikipedia.org/wiki/System_call -RedirectTemp /19-6 https://mail.python.org/pipermail/python-dev/2009-October/093356.html -RedirectTemp /19-7 http://www.dabeaz.com/finalgenerator/ -RedirectTemp /19-8 https://docs.python.org/3/library/threading.html#thread-objects -RedirectTemp /19-9 https://www.pypy.org/ -RedirectTemp /19-10 https://mail.python.org/pipermail/python-list/2009-February/675659.html -RedirectTemp /19-11 https://en.wikipedia.org/wiki/Braille_Patterns -RedirectTemp /19-12 https://docs.python.org/3/library/multiprocessing.shared_memory.html -RedirectTemp /19-13 https://docs.python.org/3/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.ShareableList -RedirectTemp /19-14 https://greenlet.readthedocs.io/en/latest/ -RedirectTemp /19-15 https://docs.sqlalchemy.org/en/14/changelog/migration_14.html#asynchronous-io-support-for-core-and-orm -RedirectTemp /19-16 https://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html -RedirectTemp /19-17 http://www.gevent.org/ -RedirectTemp /19-18 https://github.com/gevent/gevent/wiki/Projects -RedirectTemp /19-19 https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor-example -RedirectTemp /19-20 https://github.com/python/asyncio/issues/284 -RedirectTemp /19-21 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor -RedirectTemp /19-22 https://mail.python.org/archives/list/python-dev@python.org/message/JBYXQH3NV3YBF7P2HLHB5CD6V3GVTY55/ -RedirectTemp /19-23 https://docs.python.org/3/library/queue.html#queue.SimpleQueue.get -RedirectTemp /19-24 https://en.wikipedia.org/wiki/Race_condition -RedirectTemp /19-25 https://github.com/fluentpython/example-code-2e/commit/2c1230579db99738a5e5e6802063bda585f6476d -RedirectTemp /19-26 https://github.com/fluentpython/example-code-2e/blob/master/19-concurrency/primes/README.md -RedirectTemp /19-27 https://github.com/fluentpython/example-code-2e/blob/master/19-concurrency/primes/threads.py -RedirectTemp /19-28 https://en.wikipedia.org/wiki/Context_switch -RedirectTemp /19-29 http://www.gotw.ca/publications/concurrency-ddj.htm -RedirectTemp /19-30 https://www.ansible.com/ -RedirectTemp /19-31 https://saltproject.io/ -RedirectTemp /19-32 https://www.fabfile.org/ -RedirectTemp /19-33 https://engineering.fb.com/2016/05/27/production-engineering/python-in-production-engineering/ -RedirectTemp /19-34 https://jupyter.org/ -RedirectTemp /19-35 https://docs.bokeh.org/en/latest/index.html -RedirectTemp /19-36 https://www.tensorflow.org/ -RedirectTemp /19-37 https://pytorch.org/ -RedirectTemp /19-38 https://www.oreilly.com/radar/where-programming-ops-ai-and-the-cloud-are-headed-in-2021/ -RedirectTemp /19-39 https://www.youtube.com/watch?v=ods97a5Pzw0 -RedirectTemp /19-40 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy -RedirectTemp /19-41 https://modwsgi.readthedocs.io/en/master/ -RedirectTemp /19-42 https://uwsgi-docs.readthedocs.io/en/latest/ -RedirectTemp /19-43 https://unit.nginx.org/ -RedirectTemp /19-44 https://www.techatbloomberg.com/blog/configuring-uwsgi-production-deployment/ -RedirectTemp /19-45 https://www.youtube.com/watch?v=p6R1h2Nn468 -RedirectTemp /19-46 https://asgi.readthedocs.io/en/latest/index.html -RedirectTemp /19-47 https://docs.celeryproject.org/en/stable/getting-started/introduction.html -RedirectTemp /19-48 https://python-rq.org/ -RedirectTemp /19-49 https://docs.celeryproject.org/en/stable/faq.html#what-kinds-of-things-should-i-use-celery-for -RedirectTemp /19-50 https://redis.io/ -RedirectTemp /19-51 https://realpython.com/intro-to-python-threading/ -RedirectTemp /19-52 https://pymotw.com/3/concurrency.html -RedirectTemp /19-53 https://www.pearson.com/us/higher-education/program/Hellmann-Python-3-Standard-Library-by-Example-The/PGM328871.html -RedirectTemp /19-54 https://docs.python.org/3/library/multiprocessing.html#programming-guidelines -RedirectTemp /19-55 https://docs.python.org/3/library/multiprocessing.html -RedirectTemp /19-56 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ -RedirectTemp /19-57 https://link.springer.com/book/10.1007/978-1-4842-5793-7?error=cookies_not_supported&code=2ed5d61d-ae9f-4f3d-94ac-0f68cf45ea4f -RedirectTemp /19-58 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 -RedirectTemp /19-59 https://greenteapress.com/wp/semaphores/ -RedirectTemp /19-60 https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock -RedirectTemp /19-61 https://docs.python.org/3/faq/library.html#can-t-we-get-rid-of-the-global-interpreter-lock -RedirectTemp /19-62 https://www.artima.com/weblogs/viewpost.jsp?thread=214235 -RedirectTemp /19-63 http://jessenoller.com/blog/2009/02/01/python-threads-and-the-global-interpreter-lock -RedirectTemp /19-64 https://realpython.com/products/cpython-internals-book/ -RedirectTemp /19-65 http://www.dabeaz.com/GIL/ -RedirectTemp /19-66 http://www.dabeaz.com/python/UnderstandingGIL.pdf -RedirectTemp /19-67 https://bugs.python.org/issue7946#msg223110 -RedirectTemp /19-68 https://bugs.python.org/issue7946 -RedirectTemp /19-69 https://www.fullstackpython.com/ -RedirectTemp /19-70 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ -RedirectTemp /19-71 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 -RedirectTemp /19-72 https://www.packtpub.com/product/distributed-computing-with-python/9781785889691 -RedirectTemp /19-73 https://towardsdatascience.com/python-performance-and-gpus-1be860ffd58d?gi=a7d537cc2fb4 -RedirectTemp /19-74 https://instagram-engineering.com/web-service-efficiency-at-instagram-with-python-4976d078e366?gi=12a441991c88 -RedirectTemp /19-75 https://www.oreilly.com/library/view/architecture-patterns-with/9781492052197/ -RedirectTemp /19-76 https://www.cosmicpython.com/ -RedirectTemp /19-77 https://pypi.org/project/lelo/ -RedirectTemp /19-78 https://github.com/npryce/python-parallelize -RedirectTemp /19-79 https://github.com/ericsnowcurrently/multi-core-python/wiki -RedirectTemp /19-80 https://gist.github.com/markshannon/79cace3656b40e21b7021504daee950c -RedirectTemp /19-81 https://mail.python.org/archives/list/python-dev@python.org/message/YOOQZCFOKEPQ24YHWWLQSJ3RCXFMS7D7/ -RedirectTemp /19-82 https://en.wikipedia.org/wiki/Communicating_sequential_processes -RedirectTemp /19-83 https://github.com/stackless-dev/stackless/wiki -RedirectTemp /19-84 https://www.eveonline.com -RedirectTemp /19-85 https://www.ccpgames.com/ -RedirectTemp /19-86 https://stackless.readthedocs.io/en/3.6-slp/stackless-python.html#history -RedirectTemp /19-87 https://doc.pypy.org/en/latest/stackless.html -RedirectTemp /19-88 https://greenlet.readthedocs.io/en/latest/ -RedirectTemp /19-89 http://www.gevent.org/ -RedirectTemp /19-90 http://thespianpy.com/doc/ -RedirectTemp /19-91 https://pykka.readthedocs.io/en/latest/ -RedirectTemp /19-92 https://www.manning.com/books/rabbitmq-in-action -RedirectTemp /19-93 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ -RedirectTemp /19-94 https://en.wikipedia.org/wiki/OpenCL -RedirectTemp /19-95 https://media.pragprog.com/titles/pb7con/Bonus_Chapter.pdf -RedirectTemp /19-96 https://martinfowler.com/ -RedirectTemp /19-97 https://martinfowler.com/articles/patterns-of-distributed-systems/ -RedirectTemp /19-98 https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/ -RedirectTemp /19-99 https://www.oreilly.com/library/view/oscon-2016-video/9781491965153/video247021.html -RedirectTemp /19-100 https://www.oreilly.com/library/view/designing-for-scalability/9781449361556/ -RedirectTemp /19-101 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy -RedirectTemp /19-102 https://en.wikipedia.org/wiki/KISS_principle -RedirectTemp /19-103 https://www.usenix.org/conference/hotos15/workshop-program/presentation/mcsherry -############################################################ 20 -RedirectTemp /20-1 https://www.artima.com/weblogs/viewpost.jsp?thread=299551 -RedirectTemp /20-2 https://docs.python.org/3/library/http.server.html -RedirectTemp /20-3 https://www.youtube.com/watch?v=A9e9Cy1UkME -RedirectTemp /20-4 https://www.cia.gov/the-world-factbook/ -RedirectTemp /20-5 https://docs.python-requests.org/en/latest/ -RedirectTemp /20-6 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor -RedirectTemp /20-7 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed -RedirectTemp /20-8 https://docs.python.org/3/library/concurrent.futures.html -RedirectTemp /20-9 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.Executor -RedirectTemp /20-10 https://github.com/fluentpython/example-code-2e/blob/master/20-executors/getflags/flags2_common.py -RedirectTemp /20-11 https://github.com/noamraph/tqdm -RedirectTemp /20-12 https://www.youtube.com/watch?v=M8Z65tAl5l4 -RedirectTemp /20-13 https://github.com/noamraph/tqdm/blob/master/README.md -RedirectTemp /20-14 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed -RedirectTemp /20-15 https://docs.python.org/3/library/asyncio-task.html#asyncio.as_completed -RedirectTemp /20-16 https://www.cloudflare.com/ -RedirectTemp /20-17 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags -RedirectTemp /20-18 https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418 -RedirectTemp /20-19 https://en.wikipedia.org/wiki/Embarrassingly_parallel -RedirectTemp /20-20 https://pyvideo.org/video/480/pyconau-2010--the-future-is-soon/ -RedirectTemp /20-21 http://www.dabeaz.com/coroutines/ -RedirectTemp /20-22 https://en.wikipedia.org/wiki/POSIX_Threads -RedirectTemp /20-23 https://en.wikipedia.org/wiki/C_dynamic_memory_allocation -RedirectTemp /20-24 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ -RedirectTemp /20-25 https://hexdocs.pm/ecto/getting-started.html -############################################################ 21 -RedirectTemp /21-1 https://docs.python.org/3/library/asyncio.html -RedirectTemp /21-2 https://bugs.python.org/issue43216 -RedirectTemp /21-3 https://bugs.python.org/issue36921 -RedirectTemp /21-4 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.getaddrinfo -RedirectTemp /21-5 https://docs.python.org/3/library/socket.html#socket.getaddrinfo -RedirectTemp /21-6 https://docs.python.org/3.10/library/asyncio-eventloop.html#asyncio.get_event_loop -RedirectTemp /21-7 https://www.python.org/dev/peps/pep-0492/#await-expression -RedirectTemp /21-8 https://www.fluentpython.com/extra/classic-coroutines/#yield_from_meaning_sec -RedirectTemp /21-9 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags -RedirectTemp /21-10 https://magicstack.github.io/asyncpg/current/ -RedirectTemp /21-11 https://magicstack.github.io/asyncpg/current/api/index.html#transactions -RedirectTemp /21-12 https://magicstack.github.io/asyncpg/current/api/index.html#transactions -RedirectTemp /21-13 https://github.com/MagicStack/asyncpg/blob/4d39a05268ce4cc01b00458223a767542da048b8/asyncpg/transaction.py#L57 -RedirectTemp /21-14 https://magicstack.github.io/asyncpg/current/usage.html#connection-pools -RedirectTemp /21-15 https://gist.github.com/jboner/2841832 -RedirectTemp /21-16 https://en.wikipedia.org/wiki/Network-attached_storage -RedirectTemp /21-17 https://en.wikipedia.org/wiki/Semaphore_(programming) -RedirectTemp /21-18 https://en.wikipedia.org/wiki/Semaphore_(programming) -RedirectTemp /21-19 https://groups.google.com/forum/#!msg/python-tulip/PdAEtwpaJHs/7fqb-Qj2zJoJ -RedirectTemp /21-20 https://web.archive.org/web/20151209151711/http://tritarget.org/blog/2012/11/28/the-pyramid-of-doom-a-javascript-style-trap -RedirectTemp /21-21 https://stackoverflow.com/questions/53701841/what-is-the-use-case-for-future-add-done-callback/53710563 -RedirectTemp /21-22 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor -RedirectTemp /21-23 https://motor.readthedocs.io/en/stable/ -RedirectTemp /21-24 https://emptysqua.re/blog/response-to-asynchronous-python-and-databases/ -RedirectTemp /21-25 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams -RedirectTemp /21-26 https://en.wikipedia.org/wiki/Phaistos_Disc -RedirectTemp /21-27 https://en.wikipedia.org/wiki/Inverted_index -RedirectTemp /21-28 https://fastapi.tiangolo.com/ -RedirectTemp /21-29 https://swagger.io/specification/ -RedirectTemp /21-30 https://asgi.readthedocs.io/en/latest/implementations.html -RedirectTemp /21-31 https://pydantic-docs.helpmanual.io/ -RedirectTemp /21-32 https://doc.traefik.io/traefik/ -RedirectTemp /21-33 https://fastapi.tiangolo.com/project-generation/ -RedirectTemp /21-34 https://fastapi.tiangolo.com/tutorial/response-model/ -RedirectTemp /21-35 https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server -RedirectTemp /21-36 https://github.com/python/typeshed/issues/5535 -RedirectTemp /21-37 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Server.serve_forever -RedirectTemp /21-38 https://docs.python.org/3/library/asyncio-stream.html#asyncio.StreamWriter.close -RedirectTemp /21-39 https://docs.python.org/3/library/asyncio-stream.html#streamwriter -RedirectTemp /21-40 https://docs.python.org/3/library/asyncio-stream.html -RedirectTemp /21-41 https://docs.python.org/3/library/asyncio-protocol.html -RedirectTemp /21-42 https://docs.python.org/3/library/asyncio-protocol.html#tcp-echo-server -RedirectTemp /21-43 https://github.com/aio-libs/aiopg -RedirectTemp /21-44 https://docs.python.org/3/whatsnew/3.8.html#asyncio -RedirectTemp /21-45 https://datatracker.ietf.org/doc/html/rfc6761 -RedirectTemp /21-46 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager -RedirectTemp /21-47 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager -RedirectTemp /21-48 https://docs.python.org/3/library/asyncio-task.html#asyncio.gather -RedirectTemp /21-49 https://curio.readthedocs.io/en/latest/index.html -RedirectTemp /21-50 https://curio.readthedocs.io/en/latest/reference.html#task-groups -RedirectTemp /21-51 https://en.wikipedia.org/wiki/Structured_concurrency -RedirectTemp /21-52 https://mail.python.org/archives/list/python-dev@python.org/thread/2ORDAW74LGE3ZI2QETPJRT2ZL7MCCPG2/ -RedirectTemp /21-53 https://www.python.org/dev/peps/pep-0654/#motivation -RedirectTemp /21-54 https://curio.readthedocs.io/en/latest/reference.html#AWAIT -RedirectTemp /21-55 https://www.python-httpx.org/async/#curio -RedirectTemp /21-56 https://github.com/dabeaz/curio/tree/78bca8a6ad677ef51e1568ac7b3e51441ab49c42/examples -RedirectTemp /21-57 https://datatracker.ietf.org/doc/html/rfc8305 -RedirectTemp /21-58 https://trio.readthedocs.io/en/stable/ -RedirectTemp /21-59 https://www.youtube.com/watch?v=M-sc73Y-zQA -RedirectTemp /21-60 https://en.wikipedia.org/wiki/Technical_debt -RedirectTemp /21-61 https://www.youtube.com/watch?v=E-1Y4kSsAFc -RedirectTemp /21-62 https://github.com/dabeaz/curio -RedirectTemp /21-63 https://trio.readthedocs.io/en/stable/ -RedirectTemp /21-64 https://curio.readthedocs.io/en/latest/#curio-university -RedirectTemp /21-65 https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/ -RedirectTemp /21-66 https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ -RedirectTemp /21-67 https://stackoverflow.com/questions/49482969/what-is-the-core-difference-between-asyncio-and-trio -RedirectTemp /21-68 https://docs.python.org/3/library/asyncio.html -RedirectTemp /21-69 https://bugs.python.org/issue33649 -RedirectTemp /21-70 https://docs.python.org/3/library/asyncio-dev.html -RedirectTemp /21-71 https://www.youtube.com/watch?v=iG6fr81xHKA -RedirectTemp /21-72 https://www.youtube.com/watch?v=F19R_M4Nay4 -RedirectTemp /21-73 https://asherman.io/projects/unsync.html -RedirectTemp /21-74 https://pyladies.com/ -RedirectTemp /21-75 https://www.youtube.com/watch?v=sW76-pRkZk8 -RedirectTemp /21-76 https://www.youtube.com/watch?v=Xbl7XjFYsN4 -RedirectTemp /21-77 https://www.youtube.com/watch?v=02CLD-42VdI -RedirectTemp /21-78 https://micropython.org/ -RedirectTemp /21-79 https://docs.micropython.org/en/latest/library/uasyncio.html -RedirectTemp /21-80 https://www.encode.io/articles/python-async-frameworks-beyond-developer-tribalism -RedirectTemp /21-81 https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ -RedirectTemp /21-82 https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ -RedirectTemp /21-83 https://github.com/MagicStack/uvloop -RedirectTemp /21-84 http://magic.io/blog/uvloop-blazing-fast-python-networking/ -RedirectTemp /21-85 https://github.com/MagicStack/httptools -RedirectTemp /21-86 https://docs.aiohttp.org/en/stable/ -RedirectTemp /21-87 https://github.com/wg/wrk -RedirectTemp /21-88 https://twistedmatrix.com/trac/ -############################################################ 22 -RedirectTemp /22-1 https://github.com/fluentpython/example-code-2e/blob/master/22-dyn-attr-prop/oscon/data/osconfeed.json -RedirectTemp /22-2 https://pypi.org/project/attrdict/ -RedirectTemp /22-3 https://pypi.org/project/addict/ -RedirectTemp /22-4 https://github.com/ActiveState/code/tree/master/recipes/Python/52308_simple_but_handy_collector_bunch_named_stuff -RedirectTemp /22-5 https://docs.python.org/3/library/types.html#types.SimpleNamespace -RedirectTemp /22-6 https://docs.python.org/3/library/argparse.html#argparse.Namespace -RedirectTemp /22-7 https://docs.python.org/3/library/multiprocessing.html#multiprocessing.managers.Namespace -RedirectTemp /22-8 https://github.com/fluentpython/example-code-2e/blob/master/22-dyn-attr-prop/oscon/schedule_v2.py -RedirectTemp /22-9 https://docs.python.org/3/library/functools.html#functools.cached_property -RedirectTemp /22-10 https://docs.python.org/3/library/functools.html#functools.cached_property -RedirectTemp /22-11 https://bugs.python.org/issue42781 -RedirectTemp /22-12 https://docs.python.org/3/howto/descriptor.html -RedirectTemp /22-13 https://github.com/python/cpython/blob/e6d0107e13ed957109e79b796984d3d026a8660d/Lib/functools.py#L926 -RedirectTemp /22-14 https://docs.python.org/3/library/threading.html#rlock-objects -RedirectTemp /22-15 https://docs.python.org/3.10/library/functools.html#functools.cached_property -RedirectTemp /22-16 https://www.wsj.com/articles/SB10001424052970203914304576627102996831200 -RedirectTemp /22-17 https://www.youtube.com/watch?v=s35rVw1zskA&feature=youtu.be -RedirectTemp /22-18 https://docs.python.org/3/library/functions.html#dir -RedirectTemp /22-19 https://github.com/python/cpython/blob/19903085c3ad7a17c8047e1556c700f2eb109931/Lib/cmd.py#L214 -RedirectTemp /22-20 https://docs.python.org/3/library/functions.html#hasattr -RedirectTemp /22-21 https://docs.python.org/3.10/reference/datamodel.html#special-method-lookup -RedirectTemp /22-22 https://docs.python.org/3/library/functions.html -RedirectTemp /22-23 https://docs.python.org/3/reference/datamodel.html#customizing-attribute-access -RedirectTemp /22-24 https://docs.python.org/3/reference/datamodel.html#special-method-lookup -RedirectTemp /22-25 https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /22-26 http://wiki.c2.com/?WelcomeVisitors -RedirectTemp /22-27 http://wiki.c2.com/?UniformAccessPrinciple -RedirectTemp /22-28 https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html -RedirectTemp /22-29 http://www.pingo.io/docs/ -RedirectTemp /22-30 https://www.drdobbs.com/javas-new-considered-harmful/184405016 -RedirectTemp /22-31 https://www.python.org/dev/peps/pep-0008/#class-names -############################################################ 23 -RedirectTemp /23-1 http://www.aleax.it/goo_pydp.pdf -RedirectTemp /23-2 https://docs.python.org/3.10/reference/datamodel.html#implementing-descriptors -RedirectTemp /23-3 https://docs.python.org/3/howto/descriptor.html -RedirectTemp /23-4 https://docs.python.org/3/howto/ -RedirectTemp /23-5 http://www.aleax.it/Python/nylug05_om.pdf -RedirectTemp /23-6 https://www.youtube.com/watch?v=VOzvpHoYQoo -RedirectTemp /23-7 https://www.python.org/dev/peps/pep-0487/#trait-descriptors -RedirectTemp /23-8 https://dreamsongs.com/RiseOfWorseIsBetter.html -RedirectTemp /23-9 http://web.archive.org/web/20031002184114/www.amk.ca/python/writing/warts.html -RedirectTemp /23-10 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this -RedirectTemp /23-11 http://python-history.blogspot.com/2009/02/adding-support-for-user-defined-classes.html -############################################################ 24 -RedirectTemp /24-1 https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /24-2 https://docs.djangoproject.com/en/3.2/topics/db/models/#meta-options -RedirectTemp /24-3 https://www.python.org/dev/peps/pep-3155/ -RedirectTemp /24-4 https://github.com/python/cpython/blob/3.9/Lib/collections/__init__.py -RedirectTemp /24-5 https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions -RedirectTemp /24-6 https://go.dev/tour/basics/12 -RedirectTemp /24-7 https://bugs.python.org/issue42102 -RedirectTemp /24-8 https://docs.python.org/3/reference/datamodel.html#object.__init_subclass__ -RedirectTemp /24-9 https://www.python.org/dev/peps/pep-0557/#abstract -RedirectTemp /24-10 https://github.com/python/cpython/blob/3.9/Lib/dataclasses.py -RedirectTemp /24-11 https://docs.python.org/3/reference/datamodel.html#creating-the-class-object -RedirectTemp /24-12 https://mail.python.org/pipermail/python-list/2002-December/134521.html -RedirectTemp /24-13 https://mail.python.org/pipermail/python-list/2002-July/162558.html -RedirectTemp /24-14 https://github.com/fluentpython/example-code/tree/master/21-class-metaprog/bulkfood -RedirectTemp /24-15 https://en.wikipedia.org/wiki/Principle_of_least_astonishment -RedirectTemp /24-16 https://docs.python.org/3/reference/datamodel.html#object.__class_getitem__ -RedirectTemp /24-17 https://en.wikipedia.org/wiki/Trait_(computer_programming) -RedirectTemp /24-18 https://en.wikipedia.org/wiki/Aspect-oriented_programming -RedirectTemp /24-19 https://dhh.dk/arc/000416.html -RedirectTemp /24-20 https://github.com/cjrh/autoslot -RedirectTemp /24-21 https://docs.python.org/3/reference/datamodel.html#customizing-class-creation -RedirectTemp /24-22 https://docs.python.org/3/library/functions.html#type -RedirectTemp /24-23 https://docs.python.org/3/library/stdtypes.html#special-attributes -RedirectTemp /24-24 https://docs.python.org/3/library/types.html -RedirectTemp /24-25 https://www.python.org/dev/peps/pep-3129/ -RedirectTemp /24-26 https://www.youtube.com/watch?v=cAGliEJV9_o -RedirectTemp /24-27 https://docs.python.org/3/library/functools.html#functools.total_ordering -RedirectTemp /24-28 https://www.python.org/download/releases/2.2.3/descrintro/ -RedirectTemp /24-29 https://github.com/lihaoyi/macropy -RedirectTemp /24-30 https://people.eecs.berkeley.edu/~bh/ss-toc2.html diff --git a/links/deploy.sh b/links/deploy.sh deleted file mode 100755 index ced9bd9..0000000 --- a/links/deploy.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -scp FPY.LI.htaccess dh_i4p2ka@fpy.li:~/fpy.li/.htaccess diff --git a/links/sample-urls.txt b/links/sample-urls.txt deleted file mode 100644 index 9eac47c..0000000 --- a/links/sample-urls.txt +++ /dev/null @@ -1,47 +0,0 @@ -https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ -https://dask.org/ -http://example.com/1572039572038573208 -http://www.unicode.org/ -https://www.techcrunch.com/2024/startup-funding-trends -https://blog.medium.com/writing-tips-for-beginners -https://github.com/microsoft/typescript -https://stackoverflow.com/questions/javascript-async-await -https://www.reddit.com/r/programming/hot -https://docs.google.com/spreadsheets/create -https://www.youtube.com/watch?v=dQw4w9WgXcQ -https://www.amazon.com/dp/B08N5WRWNW -https://support.apple.com/iphone-setup-guide -https://www.wikipedia.org/wiki/Machine_Learning -https://www.linkedin.com/in/johndoe123 -https://www.instagram.com/p/CxYz123AbC/ -https://twitter.com/elonmusk/status/1234567890 -https://www.facebook.com/events/987654321 -https://drive.google.com/file/d/1AbCdEfGhIjKlMnOp/view -https://www.dropbox.com/s/qwerty123/document.pdf -https://zoom.us/j/1234567890?pwd=abcdef -https://calendly.com/janedoe/30min-meeting -https://www.shopify.com/admin/products/new -https://stripe.com/docs/api/charges/create -https://www.paypal.com/invoice/create -https://mailchimp.com/campaigns/dashboard -https://analytics.google.com/analytics/web/ -https://console.aws.amazon.com/s3/buckets -https://portal.azure.com/dashboard -https://www.figma.com/file/AbCdEf123456/design-system -https://www.notion.so/workspace/project-notes -https://trello.com/b/AbCdEfGh/marketing-board -https://slack.com/app_redirect?channel=general -https://discord.gg/AbCdEfGh123 -https://www.twitch.tv/streamername/videos -https://www.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M -https://www.netflix.com/browse/genre/83 -https://www.hulu.com/series/breaking-bad-2008 -https://www.airbnb.com/rooms/12345678 -https://www.booking.com/hotel/us/grand-plaza.html -https://www.expedia.com/flights/search?trip=roundtrip -https://www.uber.com/ride/request -https://www.doordash.com/store/pizza-palace-123 -https://www.grubhub.com/restaurant/tacos-el-rey-456 -https://www.zillow.com/homes/for_sale/San-Francisco-CA -https://www.craigslist.org/about/sites -https://www.python.org/dev/peps/pep-0484/ \ No newline at end of file diff --git a/links/short.htaccess b/links/short.htaccess deleted file mode 100644 index 86fe3e2..0000000 --- a/links/short.htaccess +++ /dev/null @@ -1,14 +0,0 @@ -# content of short.htaccess file created and managed by short.py - -# appended: 2025-05-23 15:12:13 -RedirectTemp /22 https://pythonfluente.com/2/#pattern_matching_case_study_sec -RedirectTemp /23 https://pythonfluente.com/2/#how_slicing_works -RedirectTemp /24 https://pythonfluente.com/2/#sliceable_sequence -RedirectTemp /25 https://pythonfluente.com/2/#virtual_subclass_sec -RedirectTemp /26 https://pythonfluente.com/2/#environment_class_ex -RedirectTemp /27 https://pythonfluente.com/2/#subclass_builtin_woes -RedirectTemp /28 https://pythonfluente.com/2/#slots_section -RedirectTemp /29 https://pythonfluente.com/2/#typeddict_sec -RedirectTemp /2a https://pythonfluente.com/2/#problems_annot_runtime_sec -RedirectTemp /2b https://pythonfluente.com/2/#legacy_deprecated_typing_box -RedirectTemp /2c https://pythonfluente.com/2/#positional_pattern_implement_sec diff --git a/links/short.py b/links/short.py deleted file mode 100755 index d67c71d..0000000 --- a/links/short.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python3 - -""" -short.py generates unique short URLs. - -This script reads lines from stdin or files named as arguments, then: - -1. retrieves or creates new short URLs, taking into account existing RedirectTemp - directives in custom.htaccess or short.htaccess; -2. appends RedirectTemp directives for newly created short URLs to short.htaccess; -3. outputs the list of (short, long) URLs retrieved or created. - -""" - -import fileinput -import itertools -from collections.abc import Iterator -from time import strftime - -HTACCESS_CUSTOM = 'custom.htaccess' -HTACCESS_SHORT = 'short.htaccess' -HTACCESS_FILES = (HTACCESS_CUSTOM, HTACCESS_SHORT) -BASE_DOMAIN = 'fpy.li' - - -def load_redirects() -> tuple[dict, dict]: - redirects = {} - targets = {} - for filename in HTACCESS_FILES: - with open(filename) as fp: - for line in fp: - if line.startswith('RedirectTemp'): - _, short, long = line.split() - short = short[1:] # Remove leading slash - assert short not in redirects, f'{filename}: duplicate redirect from {short}' - # htaccess.custom is live since 2022, we can't change it remove duplicate targets - if filename != HTACCESS_CUSTOM: - assert long not in targets, f'{filename}: duplicate redirect to {long}' - redirects[short] = long - targets[long] = short - return redirects, targets - - -SDIGITS = '23456789abcdefghjkmnpqrstvwxyz' - - -def gen_short(start_len=1) -> Iterator[str]: - """Generate every possible sequence of SDIGITS, starting with start_len""" - length = start_len - while True: - for short in itertools.product(SDIGITS, repeat=length): - yield ''.join(short) - length += 1 - - -def gen_unused_short(redirects: dict) -> Iterator[str]: - """Generate next available short URL of len >= 2.""" - for short in gen_short(2): - if short not in redirects: - yield short - - -def shorten(urls: list[str]) -> list[tuple[str, str]]: - """Return (short, long) pairs, appending directives to HTACCESS_SHORT as needed.""" - redirects, targets = load_redirects() - iter_short = gen_unused_short(redirects) - pairs = [] - timestamp = strftime('%Y-%m-%d %H:%M:%S') - with open(HTACCESS_SHORT, 'a') as fp: - for long in urls: - assert BASE_DOMAIN not in long, f'{long} is a {BASE_DOMAIN} URL' - if long in targets: - short = targets[long] - else: - short = next(iter_short) - redirects[short] = long - targets[long] = short - if timestamp: - fp.write(f'\n# appended: {timestamp}\n') - timestamp = None - fp.write(f'RedirectTemp /{short} {long}\n') - pairs.append((short, long)) - - return pairs - - -def main() -> None: - """read URLS from filename arguments or stdin""" - urls = [line.strip() for line in fileinput.input(encoding='utf-8')] - for short, long in shorten(urls): - print(f'{BASE_DOMAIN}/{short}\t{long}') - - -if __name__ == '__main__': - main()