├── .gitattributes
├── Chapter10
├── 10_02_simple_socket.py
├── 10_03_simple_client.py
├── 10_04_logging_decorator.py
├── 10_05_gzip_decorator.py
├── 10_06_calling_decorated_sockets.py
├── 10_09_logging_decorator.py
├── 10_10_decorator_syntax.py
├── 10_11_observer_core.py
├── 10_12_observer_observing.py
├── 10_13_strategy_tile.py
├── 10_14_simple_xml_to_parse.xml
├── 10_15_xml_states.py
├── 10_16_singleton_using_new.py
├── 10_17_xml_singletonstates.py
├── 10_18_create_database_for_template.py
├── 10_19_template_abstract_noimple.py
├── 10_20_template_abstract_implemented.py
├── 10_21_template_concretes.py
├── Benson.jpg
├── TILED.jpg
└── sales.db
├── Chapter11
├── 11_01_age_calculator.py
├── 11_02_age_calculator_adapted.py
├── 11_03_age_calculator_adapt_date.py
├── 11_04_email_facade.py
├── 11_05_flyweight_factory.py
├── 11_06_flyweight_init.py
├── 11_07_flyweight_check_serial.py
├── 11_08_car_class.py
├── 11_09_window_commands.py
├── 11_10_window_command_invokers.py
├── 11_11_window_command_commands.py
├── 11_12_window_command_function.py
├── 11_13_document_command_callable.py
├── 11_14_formatters.py
├── 11_15_formatter_factories.py
├── 11_16_composite_folder_methods.py
├── 11_17_component_hierarchy.py
└── 11_18_add_child.py
├── Chapter12
├── 12_01_simplest_unittest.py
├── 12_02_assertraises_python.py
├── 12_03_stats.py
├── 12_04_test_stats.py
├── 12_05_skipping_tests.py
├── 12_06_simplestpytest.py
├── 12_06_simplestpytest.pyc
├── 12_07_class_pytest.py
├── 12_07_class_pytest.pyc
├── 12_08_setup_teardown.py
├── 12_08_setup_teardown.pyc
├── 12_09_funcargs.py
├── 12_09_funcargs.pyc
├── 12_10_funcarg_finalizer.py
├── 12_10_funcarg_finalizer.pyc
├── 12_11_echo_server.py
├── 12_12_pytest_echo.py
├── 12_12_pytest_echo.pyc
├── 12_13_pytest_simple_skip.py
├── 12_13_pytest_simple_skip.pyc
├── 12_14_pytest_importorskip.py
├── 12_14_pytest_importorskip.pyc
├── 12_15_pytest_skipifmark.py
├── 12_15_pytest_skipifmark.pyc
├── 12_16_mock_flightstatus.py
├── 12_17_mock_redis.py
├── 12_30_coverage_unittest.py
├── __pycache__
│ ├── stats.cpython-34.pyc
│ └── test_mock.cpython-34-PYTEST.pyc
├── casestudy
│ ├── __pycache__
│ │ ├── test_vigenere_cipher.cpython-34-PYTEST.pyc
│ │ └── vigenere_cipher.cpython-34.pyc
│ ├── test_vigenere_cipher.py
│ ├── test_vigenere_cipher.pyc
│ ├── vigenere_cipher.py
│ ├── vigenere_cipher.pyc
│ └── vigenere_cipher1.py
├── dump.rdb
├── stats.py
├── stats.pyc
└── test_mock.py
├── Chapter13
├── 13_01_two_basic_threads.py
├── 13_02_thread_wait.py
├── 13_03_parallel.py
├── 13_04_prime_factor.py
├── 13_05_post_search.py
├── 13_06_futures.py
├── 13_07_basic_async.py
├── 13_08_asyncio_dns.py
├── 13_09_async_multiprocessing.py
├── 13_10_async_client.py
└── case_study
│ ├── big.bmp
│ ├── big2.bmp
│ ├── big3.bmp
│ ├── big4.bmp
│ ├── big5.bmp
│ ├── big6.bmp
│ ├── big7.bmp
│ ├── big8.bmp
│ ├── bricks.bmp
│ ├── compress_bmp.py
│ ├── decompress_to_bmp.py
│ └── row.bmp
├── Chapter3
├── 3_01_inheriting_from_object.py
├── 3_02_simple_contact_class_to_inherit_from.py
├── 3_03_contact_inherit_supplier.py
├── 3_04_contact_list_inheritance.py
├── 3_05_dictionary_long_name.py
├── 3_06_friend_overrides_init.py
├── 3_07_friend_overrides_init_super.py
├── 3_08_send_mail.py
├── 3_09_send_mail_multi.py
├── 3_10_friend_address_holder.py
├── 3_11_friend_multi.py
├── 3_12_contrived_diamond.py
├── 3_14_contrived_diamond_super.py
├── 3_15_friend_multi_super.py
├── 3_16_polymorphic_audio.py
├── 3_17.1_abc_container.py
├── 3_17.2_abc_media.py
├── 3_17_ducktype_flac.py
├── 3_18_property.py
├── 3_19_apartment_ugly_prompt.py
├── 3_20_validation_function.py
├── 3_21_apartment_nice_prompt.py
├── 3_22_house.py
├── 3_23_purchase_and_rental.py
├── 3_24_house_rental.py
├── 3_25_remaining_subclasses.py
├── 3_26_rudimentary_agent.py
├── 3_27_type_map.py
├── 3_28_add_property.py
└── final_case_study.py
├── Chapter4
├── 04_03_method_calls_excepting.py
├── 4_01_even_integers.py
├── 4_02_exception_quits.py
├── 4_04_try_except.py
├── 4_05_catch_specific_exception.py
├── 4_06_catch_multiple_exceptions.py
├── 4_07_catch_multiple_different.py
├── 4_08_catch_as_keyword.py
├── 4_09_finally_and_else.py
├── 4_10_defining_an_exception.py
├── 4_11_exception_with_custom_args.py
├── 4_12_handle_custom_exception.py
├── 4_13_branching_vs_exceptions.py
├── 4_14_inventory_mock_object.py
├── 4_15_inventory_handling.py
├── 4_16_auth_user.py
├── 4_17_authenticator.py
├── 4_18_login.py
├── 4_19_authorizor.py
├── 4_20_test_auth.py
├── __pycache__
│ └── auth.cpython-34.pyc
├── auth.py
└── auth.pyc
├── Chapter5
├── 5_01_distances_no_objects.py
├── 5_02_distances_by_object.py
├── 5_03_object_polygon_init.py
├── 5_04_pytho_ugly_as_java.py
├── 5_05_python_pretty_as_python.py
├── 5_06_setting_name_in_method.py
├── 5_07_setting_name_property.py
├── 5_08_property_arguments.py
├── 5_09_property_decorator_get.py
├── 5_11_property_decorator_arguments.py
├── 5_12_property_decorator_get_set.py
├── 5_13_read_only_setattr.py
├── 5_14_read_only_getattribute.py
├── 5_15_cache_getter.py
├── 5_16_average_property.py
├── 5_17_zipsearch.py
├── 5_18_zipprocessor.py
├── 5_19_zipreplace_inheritance.py
├── 5_20_scaleimage_inheritance.py
├── 5_24_most_basic_document.py
├── 5_25_document_cursor.py
├── 5_26_document_using_cursor.py
├── 5_27_string_property.py
├── 5_28_Character_class.py
├── 5_29_document_with_character.py
├── Document.py
├── zip_processor.py
└── zip_processor.pyc
├── Chapter6
├── 6_01_empty_object.py
├── 6_02_pass_tuple_to_function.py
├── 6_03_named_tuple.py
├── 6_04_dict_stocks.py
├── 6_05_random_key_dict.py
├── 6_06_setdefault_frequency.py
├── 6_07_defaultdict_frequency.py
├── 6_08_defaultdict_custom_function.py
├── 6_09_counter_frequency.py
├── 6_09_counter_poll.py
├── 6_09_list_tuple_frequency.py
├── 6_10_object_comparison.py
├── 6_11_song_artist_set.py
├── 6_12_set_operations.py
├── 6_13_set_operations2.py
├── 6_14_oop_pairs.py
├── 6_15_stupid_adding_integer.py
├── 6_16_dictsorted.py
├── 6_17_link_parser.py
├── 6_18_normalize_url.py
├── 6_19_visited_links_sets.py
├── 6_20_collect_remaining_links.py
├── 6_21_print_collected_links.py
├── 6_22_dict_link_collector.py
├── 6_23_queue_link_collector.py
├── case_study_serve
│ ├── blog.html
│ ├── contact.html
│ ├── esme.html
│ ├── hobbies.html
│ ├── index.html
│ └── yoga.html
└── link_collector.py
├── Chapter7
├── 7_01_reversible_objects.py
├── 7_02_enumerate_line_numbers.py
├── 7_03.1_zip_to_enumerate.py
├── 7_03_enumerate_max_min.py
├── 7_04_tdf_contact.txt
├── 7_05_read_file.py
├── 7_05_tdf_processor.py
├── 7_06_write.py
├── 7_07_with.py
├── 7_08_context_manager.py
├── 7_18_bad_kw_default.py
├── 7_19_link_downloader.py
├── 7_20_link_downloader_vararg.py
├── 7_21_kwarg_options.py
├── 7_22_all_arguments.py
├── 7_23_unpacking_arguments.py
├── 7_24_function_object.py
├── 7_25_timer.py
├── 7_26_timer_test.py
├── 7_27_add_function_to_object.py
├── 7_28_callable_repeat.py
├── 7_29_send_email.py
├── 7_30_send_email_dict_headers.py
├── 7_32_mailing_list_defaultdict_set.py
├── 7_33_mailing_list_get_emails.py
├── 7_34_send_mailing.py
├── 7_35_load_save.py
├── 7_36_enter_exit.py
├── __pycache__
│ └── timer.cpython-34.pyc
├── addresses.db
├── mailing_list.py
├── timer.py
└── timer.pyc
├── Chapter8
├── 8_01_string_creation.py
├── 8_02_format_empty.py
├── 8_03_format_position.py
├── 8_04_format_some_positions_broken.py
├── 8_05_brace_escape.py
├── 8_06_format_kw_args.py
├── 8_07_unlabelled_kw.py
├── 8_08_tuple_dict_format.py
├── 8_09_tuple_in_dict_format.py
├── 8_10_object_formatting.py
├── 8_11_no_format.py
├── 8_12_currency_format.py
├── 8_13_tabular.py
├── 8_14_format_datetime.py
├── 8_15_encode_bytes.py
├── 8_16_decode_unicode.py
├── 8_17_bytearray_replace.py
├── 8_18_bytearray_index.py
├── 8_23.1_basic_regex.py
├── 8_23.2_regex_generic.py
├── 8_23.3_match_group.py
├── 8_23_stringio.py
├── 8_24_basic_pickling.py
├── 8_25_state_pickling.py
├── 8_26_json_objects.py
├── 8_27_template_boilerplate.py
├── 8_28_template_process.py
├── 8_29_template_processer_complete.py
└── case_study_input
│ ├── context.json
│ ├── footer.html
│ ├── header.html
│ ├── main.html
│ └── menu.html
├── Chapter9
├── 9_01_iterator.py
├── 9_06_for_loop_converter.py
├── 9_07_list_comp_converter.py
├── 9_08_list_comp_exclude.py
├── 9_09_tdf_list_comp.py
├── 9_10_set_comprehension.py
├── 9_11_dict_comprehension.py
├── 9_12_log_file.log
├── 9_13_log_processor.py
├── 9_14_log_delete_warning_expression.py
├── 9_15_log_delete_warnings_loop.py
├── 9_16_log_delete_warnings_object.py
├── 9_17_log_delete_warnings_generator.py
├── 9_18_log_delete_warnings_yield_from.py
├── 9_19_yield_from_filesystem.py
├── 9_20_basic_count_coroutine.py
├── 9_21_kernel_log.py
├── 9_22_load_dataset.py
├── 9_23_generate_colors.py
├── 9_24_color_distance.py
├── 9_25_knearest.py
├── 9_26_write_results.py
├── EXAMPLE_LOG.log
├── case_study_machine_learn.py
├── colors.csv
├── example.log
├── kivy_color_checker
│ ├── colorchecker.kv
│ ├── main.py
│ └── output.csv
├── kivy_color_classifier
│ ├── colorclassifier.kv
│ └── main.py
└── output.csv
├── LICENSE
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.bmp filter=lfs diff=lfs merge=lfs -text
2 |
--------------------------------------------------------------------------------
/Chapter10/10_02_simple_socket.py:
--------------------------------------------------------------------------------
1 | import socket
2 |
3 | def respond(client):
4 | response = input("Enter a value: ")
5 | client.send(bytes(response, 'utf8'))
6 | client.close()
7 |
8 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
9 | server.bind(('localhost',2401))
10 | server.listen(1)
11 | try:
12 | while True:
13 | client, addr = server.accept()
14 | respond(client)
15 | finally:
16 | server.close()
17 |
--------------------------------------------------------------------------------
/Chapter10/10_03_simple_client.py:
--------------------------------------------------------------------------------
1 | import socket
2 |
3 | client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
4 | client.connect(('localhost', 2401))
5 | print("Received: {0}".format(client.recv(1024)))
6 | client.close()
7 |
8 |
--------------------------------------------------------------------------------
/Chapter10/10_04_logging_decorator.py:
--------------------------------------------------------------------------------
1 | import socket
2 |
3 | class LogSocket:
4 | def __init__(self, socket):
5 | self.socket = socket
6 |
7 | def send(self, data):
8 | print("Sending {0} to {1}".format(
9 | data, self.socket.getpeername()[0]))
10 | self.socket.send(data)
11 |
12 | def close(self):
13 | self.socket.close()
14 |
15 | def respond(client):
16 | response = input("Enter a value: ")
17 | client.send(bytes(response, 'utf8'))
18 | client.close()
19 |
20 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
21 | server.bind(('',2401))
22 | server.listen(1)
23 | try:
24 | while True:
25 | client, addr = server.accept()
26 | respond(LogSocket(client))
27 | finally:
28 | server.close()
29 |
--------------------------------------------------------------------------------
/Chapter10/10_05_gzip_decorator.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import gzip
3 | from io import BytesIO
4 |
5 | class GzipSocket:
6 | def __init__(self, socket):
7 | self.socket = socket
8 |
9 | def send(self, data):
10 | buf = BytesIO()
11 | zipfile = gzip.GzipFile(fileobj=buf, mode="w")
12 | zipfile.write(data)
13 | zipfile.close()
14 | self.socket.send(buf.getvalue())
15 |
16 | def close(self):
17 | self.socket.close()
18 |
19 | def respond(client):
20 | response = input("Enter a value: ")
21 | client.send(bytes(response, 'utf8'))
22 | client.close()
23 |
24 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
25 | server.bind(('',2401))
26 | server.listen(1)
27 | try:
28 | while True:
29 | client, addr = server.accept()
30 | respond(GzipSocket(client))
31 | finally:
32 | server.close()
33 |
--------------------------------------------------------------------------------
/Chapter10/10_06_calling_decorated_sockets.py:
--------------------------------------------------------------------------------
1 | client, addr = server.accept()
2 | if log_send:
3 | client = LoggingSocket(client)
4 | if client.getpeername()[0] in compress_hosts:
5 | client = GzipSocket(client)
6 | respond(client)
7 |
--------------------------------------------------------------------------------
/Chapter10/10_09_logging_decorator.py:
--------------------------------------------------------------------------------
1 | import time
2 | def log_calls(func):
3 | def wrapper(*args, **kwargs):
4 | now = time.time()
5 | print("Calling {0} with {1} and {2}".format(
6 | func.__name__, args, kwargs))
7 | return_value = func(*args, **kwargs)
8 | print("Executed {0} in {1}ms".format(
9 | func.__name__, time.time() - now))
10 | return return_value
11 | return wrapper
12 |
13 | def test1(a,b,c):
14 | print("\ttest1 called")
15 |
16 | def test2(a,b):
17 | print("\ttest2 called")
18 |
19 | def test3(a,b):
20 | print("\ttest3 called")
21 | time.sleep(1)
22 |
23 | test1 = log_calls(test1)
24 | test2 = log_calls(test2)
25 | test3 = log_calls(test3)
26 |
27 | test1(1,2,3)
28 | test2(4,b=5)
29 | test3(6,7)
30 |
--------------------------------------------------------------------------------
/Chapter10/10_10_decorator_syntax.py:
--------------------------------------------------------------------------------
1 | @log_calls
2 | def test1(a,b,c):
3 | print("\ttest1 called")
4 |
--------------------------------------------------------------------------------
/Chapter10/10_11_observer_core.py:
--------------------------------------------------------------------------------
1 | class Inventory:
2 | def __init__(self):
3 | self.observers = []
4 | self._product = None
5 | self._quantity = 0
6 |
7 | def attach(self, observer):
8 | self.observers.append(observer)
9 |
10 | @property
11 | def product(self):
12 | return self._product
13 | @product.setter
14 | def product(self, value):
15 | self._product = value
16 | self._update_observers()
17 |
18 | @property
19 | def quantity(self):
20 | return self._quantity
21 | @quantity.setter
22 | def quantity(self, value):
23 | self._quantity = value
24 | self._update_observers()
25 |
26 | def _update_observers(self):
27 | for observer in self.observers:
28 | observer()
29 |
--------------------------------------------------------------------------------
/Chapter10/10_12_observer_observing.py:
--------------------------------------------------------------------------------
1 | class Inventory:
2 | def __init__(self):
3 | self.observers = []
4 | self._product = None
5 | self._quantity = 0
6 |
7 | def attach(self, observer):
8 | self.observers.append(observer)
9 |
10 | @property
11 | def product(self):
12 | return self._product
13 | @product.setter
14 | def product(self, value):
15 | self._product = value
16 | self._update_observers()
17 |
18 | @property
19 | def quantity(self):
20 | return self._quantity
21 | @quantity.setter
22 | def quantity(self, value):
23 | self._quantity = value
24 | self._update_observers()
25 |
26 | def _update_observers(self):
27 | for observer in self.observers:
28 | observer()
29 |
30 | class ConsoleObserver:
31 | def __init__(self, inventory):
32 | self.inventory = inventory
33 |
34 | def __call__(self):
35 | print(self.inventory.product)
36 | print(self.inventory.quantity)
37 |
--------------------------------------------------------------------------------
/Chapter10/10_13_strategy_tile.py:
--------------------------------------------------------------------------------
1 | from PIL import Image
2 |
3 |
4 | class TiledStrategy:
5 | def make_background(self, img_file, desktop_size):
6 | in_img = Image.open(img_file)
7 | out_img = Image.new('RGB', desktop_size)
8 | num_tiles = [
9 | o // i + 1 for o, i in
10 | zip(out_img.size, in_img.size)
11 | ]
12 | for x in range(num_tiles[0]):
13 | for y in range(num_tiles[1]):
14 | out_img.paste(
15 | in_img,
16 | (
17 | in_img.size[0] * x,
18 | in_img.size[1] * y,
19 | in_img.size[0] * (x+1),
20 | in_img.size[1] * (y+1)
21 | )
22 | )
23 | return out_img
24 |
25 |
26 | class CenteredStrategy:
27 | def make_background(self, img_file, desktop_size):
28 | in_img = Image.open(img_file)
29 | out_img = Image.new('RGB', desktop_size)
30 | left = (out_img.size[0] - in_img.size[0]) // 2
31 | top = (out_img.size[1] - in_img.size[1]) // 2
32 | out_img.paste(
33 | in_img,
34 | (
35 | left,
36 | top,
37 | left+in_img.size[0],
38 | top + in_img.size[1]
39 | )
40 | )
41 | return out_img
42 |
43 |
44 | class ScaledStrategy:
45 | def make_background(self, img_file, desktop_size):
46 | in_img = Image.open(img_file)
47 | out_img = in_img.resize(desktop_size)
48 | return out_img
49 |
--------------------------------------------------------------------------------
/Chapter10/10_14_simple_xml_to_parse.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dusty Phillips
4 | Packt Publishing
5 | Python 3 Object Oriented Programming
6 |
7 |
8 | 1
9 | Object Oriented Design
10 |
11 |
12 | 2
13 | Objects In Python
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Chapter10/10_15_xml_states.py:
--------------------------------------------------------------------------------
1 | class Node:
2 | def __init__(self, tag_name, parent=None):
3 | self.parent = parent
4 | self.tag_name = tag_name
5 | self.children = []
6 | self.text=""
7 |
8 | def __str__(self):
9 | if self.text:
10 | return self.tag_name + ": " + self.text
11 | else:
12 | return self.tag_name
13 |
14 | class FirstTag:
15 | def process(self, remaining_string, parser):
16 | i_start_tag = remaining_string.find('<')
17 | i_end_tag = remaining_string.find('>')
18 | tag_name = remaining_string[i_start_tag+1:i_end_tag]
19 | root = Node(tag_name)
20 | parser.root = parser.current_node = root
21 | parser.state = ChildNode()
22 | return remaining_string[i_end_tag+1:]
23 |
24 | class ChildNode:
25 | def process(self, remaining_string, parser):
26 | stripped = remaining_string.strip()
27 | if stripped.startswith(""):
28 | parser.state = CloseTag()
29 | elif stripped.startswith("<"):
30 | parser.state = OpenTag()
31 | else:
32 | parser.state = TextNode()
33 | return stripped
34 |
35 | class OpenTag:
36 | def process(self, remaining_string, parser):
37 | i_start_tag = remaining_string.find('<')
38 | i_end_tag = remaining_string.find('>')
39 | tag_name = remaining_string[i_start_tag+1:i_end_tag]
40 | node = Node(tag_name, parser.current_node)
41 | parser.current_node.children.append(node)
42 | parser.current_node = node
43 | parser.state = ChildNode()
44 | return remaining_string[i_end_tag+1:]
45 |
46 | class TextNode:
47 | def process(self, remaining_string, parser):
48 | i_start_tag = remaining_string.find('<')
49 | text = remaining_string[:i_start_tag]
50 | parser.current_node.text = text
51 | parser.state = ChildNode()
52 | return remaining_string[i_start_tag:]
53 |
54 | class CloseTag:
55 | def process(self, remaining_string, parser):
56 | i_start_tag = remaining_string.find('<')
57 | i_end_tag = remaining_string.find('>')
58 | assert remaining_string[i_start_tag+1] == "/"
59 | tag_name = remaining_string[i_start_tag+2:i_end_tag]
60 | assert tag_name == parser.current_node.tag_name
61 | parser.current_node = parser.current_node.parent
62 | parser.state = ChildNode()
63 | return remaining_string[i_end_tag+1:].strip()
64 |
65 | class Parser:
66 | def __init__(self, parse_string):
67 | self.parse_string = parse_string
68 | self.root = None
69 | self.current_node = None
70 |
71 | self.state = FirstTag()
72 |
73 | def process(self, remaining_string):
74 | remaining = self.state.process(remaining_string, self)
75 | if remaining:
76 | self.process(remaining)
77 |
78 | def start(self):
79 | self.process(self.parse_string)
80 |
81 | if __name__ == "__main__":
82 | import sys
83 | with open(sys.argv[1]) as file:
84 | contents = file.read()
85 | p = Parser(contents)
86 | p.start()
87 |
88 | nodes = [p.root]
89 | while nodes:
90 | node = nodes.pop(0)
91 | print(node)
92 | nodes = node.children + nodes
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/Chapter10/10_16_singleton_using_new.py:
--------------------------------------------------------------------------------
1 | class OneOnly:
2 | _singleton = None
3 | def __new__(cls, *args, **kwargs):
4 | if not cls._singleton:
5 | cls._singleton = super(OneOnly, cls).__new__(
6 | cls, *args, **kwargs)
7 | return cls._singleton
8 |
--------------------------------------------------------------------------------
/Chapter10/10_17_xml_singletonstates.py:
--------------------------------------------------------------------------------
1 | class Node:
2 | def __init__(self, tag_name, parent=None):
3 | self.parent = parent
4 | self.tag_name = tag_name
5 | self.children = []
6 | self.text=""
7 |
8 | def __str__(self):
9 | if self.text:
10 | return self.tag_name + ": " + self.text
11 | else:
12 | return self.tag_name
13 |
14 | class FirstTag:
15 | def process(self, remaining_string, parser):
16 | i_start_tag = remaining_string.find('<')
17 | i_end_tag = remaining_string.find('>')
18 | tag_name = remaining_string[i_start_tag+1:i_end_tag]
19 | root = Node(tag_name)
20 | parser.root = parser.current_node = root
21 | parser.state = child_node
22 | return remaining_string[i_end_tag+1:]
23 |
24 | class ChildNode:
25 | def process(self, remaining_string, parser):
26 | stripped = remaining_string.strip()
27 | if stripped.startswith(""):
28 | parser.state = close_tag
29 | elif stripped.startswith("<"):
30 | parser.state = open_tag
31 | else:
32 | parser.state = text_node
33 | return stripped
34 |
35 | class OpenTag:
36 | def process(self, remaining_string, parser):
37 | i_start_tag = remaining_string.find('<')
38 | i_end_tag = remaining_string.find('>')
39 | tag_name = remaining_string[i_start_tag+1:i_end_tag]
40 | node = Node(tag_name, parser.current_node)
41 | parser.current_node.children.append(node)
42 | parser.current_node = node
43 | parser.state = child_node
44 | return remaining_string[i_end_tag+1:]
45 |
46 | class TextNode:
47 | def process(self, remaining_string, parser):
48 | i_start_tag = remaining_string.find('<')
49 | text = remaining_string[:i_start_tag]
50 | parser.current_node.text = text
51 | parser.state = child_node
52 | return remaining_string[i_start_tag:]
53 |
54 | class CloseTag:
55 | def process(self, remaining_string, parser):
56 | i_start_tag = remaining_string.find('<')
57 | i_end_tag = remaining_string.find('>')
58 | assert remaining_string[i_start_tag+1] == "/"
59 | tag_name = remaining_string[i_start_tag+2:i_end_tag]
60 | assert tag_name == parser.current_node.tag_name
61 | parser.current_node = parser.current_node.parent
62 | parser.state = child_node
63 | return remaining_string[i_end_tag+1:].strip()
64 |
65 | first_tag = FirstTag()
66 | child_node = ChildNode()
67 | text_node = TextNode()
68 | open_tag = OpenTag()
69 | close_tag = CloseTag()
70 |
71 | class Parser:
72 | def __init__(self, parse_string):
73 | self.parse_string = parse_string
74 | self.root = None
75 | self.current_node = None
76 |
77 | self.state = first_tag
78 |
79 | def process(self, remaining_string):
80 | remaining = self.state.process(remaining_string, self)
81 | if remaining:
82 | self.process(remaining)
83 |
84 | def start(self):
85 | self.process(self.parse_string)
86 |
87 | if __name__ == "__main__":
88 | import sys
89 | with open(sys.argv[1]) as file:
90 | contents = file.read()
91 | p = Parser(contents)
92 | p.start()
93 |
94 | nodes = [p.root]
95 | while nodes:
96 | node = nodes.pop(0)
97 | print(node)
98 | nodes = node.children + nodes
99 |
--------------------------------------------------------------------------------
/Chapter10/10_18_create_database_for_template.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 |
3 | conn = sqlite3.connect("sales.db")
4 |
5 | conn.execute("CREATE TABLE Sales (salesperson text, "
6 | "amt currency, year integer, model text, new boolean)")
7 | conn.execute("INSERT INTO Sales values"
8 | " ('Tim', 16000, 2010, 'Honda Fit', 'true')")
9 | conn.execute("INSERT INTO Sales values"
10 | " ('Tim', 9000, 2006, 'Ford Focus', 'false')")
11 | conn.execute("INSERT INTO Sales values"
12 | " ('Gayle', 8000, 2004, 'Dodge Neon', 'false')")
13 | conn.execute("INSERT INTO Sales values"
14 | " ('Gayle', 28000, 2009, 'Ford Mustang', 'true')")
15 | conn.execute("INSERT INTO Sales values"
16 | " ('Gayle', 50000, 2010, 'Lincoln Navigator', 'true')")
17 | conn.execute("INSERT INTO Sales values"
18 | " ('Don', 20000, 2008, 'Toyota Prius', 'false')")
19 | conn.commit()
20 | conn.close()
21 |
--------------------------------------------------------------------------------
/Chapter10/10_19_template_abstract_noimple.py:
--------------------------------------------------------------------------------
1 | class QueryTemplate:
2 | def connect(self):
3 | pass
4 | def construct_query(self):
5 | pass
6 | def do_query(self):
7 | pass
8 | def format_results(self):
9 | pass
10 | def output_results(self):
11 | pass
12 |
13 | def process_format(self):
14 | self.connect()
15 | self.construct_query()
16 | self.do_query()
17 | self.format_results()
18 | self.output_results()
19 |
--------------------------------------------------------------------------------
/Chapter10/10_20_template_abstract_implemented.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 |
3 | class QueryTemplate:
4 | def connect(self):
5 | self.conn = sqlite3.connect("sales.db")
6 |
7 | def construct_query(self):
8 | raise NotImplementedError()
9 |
10 | def do_query(self):
11 | results = self.conn.execute(self.query)
12 | self.results = results.fetchall()
13 |
14 | def format_results(self):
15 | output = []
16 | for row in self.results:
17 | row =[str(i) for i in row]
18 | output.append(", ".join(row))
19 | self.formatted_results = "\n".join(output)
20 |
21 | def output_results(self):
22 | raise NotImplementedError()
23 |
24 | def process_format(self):
25 | self.connect()
26 | self.construct_query()
27 | self.do_query()
28 | self.format_results()
29 | self.output_results()
30 |
--------------------------------------------------------------------------------
/Chapter10/10_21_template_concretes.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 |
3 | class QueryTemplate:
4 | def connect(self):
5 | self.conn = sqlite3.connect("sales.db")
6 |
7 | def construct_query(self):
8 | raise NotImplementedError()
9 |
10 | def do_query(self):
11 | results = self.conn.execute(self.query)
12 | self.results = results.fetchall()
13 |
14 | def format_results(self):
15 | output = []
16 | for row in self.results:
17 | row =[str(i) for i in row]
18 | output.append(", ".join(row))
19 | self.formatted_results = "\n".join(output)
20 |
21 | def output_results(self):
22 | raise NotImplementedError()
23 |
24 | def process_format(self):
25 | self.connect()
26 | self.construct_query()
27 | self.do_query()
28 | self.format_results()
29 | self.output_results()
30 |
31 | import datetime
32 | class NewVehiclesQuery(QueryTemplate):
33 | def construct_query(self):
34 | self.query = "select * from Sales where new='true'"
35 |
36 | def output_results(self):
37 | print(self.formatted_results)
38 |
39 | class UserGrossQuery(QueryTemplate):
40 | def construct_query(self):
41 | self.query = ("select salesperson, sum(amt) " +
42 | " from Sales group by salesperson")
43 |
44 | def output_results(self):
45 | filename = "gross_sales_{0}".format(
46 | datetime.date.today().strftime("%Y%m%d")
47 | )
48 | with open(filename, 'w') as outfile:
49 | outfile.write(self.formatted_results)
50 |
--------------------------------------------------------------------------------
/Chapter10/Benson.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter10/Benson.jpg
--------------------------------------------------------------------------------
/Chapter10/TILED.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter10/TILED.jpg
--------------------------------------------------------------------------------
/Chapter10/sales.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter10/sales.db
--------------------------------------------------------------------------------
/Chapter11/11_01_age_calculator.py:
--------------------------------------------------------------------------------
1 | class AgeCalculator:
2 | def __init__(self, birthday):
3 | self.year, self.month, self.day = (
4 | int(x) for x in birthday.split('-'))
5 |
6 | def calculate_age(self, date):
7 | year, month, day = (
8 | int(x) for x in date.split('-'))
9 | age = year - self.year
10 | if (month,day) < (self.month,self.day):
11 | age -= 1
12 | return age
13 |
--------------------------------------------------------------------------------
/Chapter11/11_02_age_calculator_adapted.py:
--------------------------------------------------------------------------------
1 | class AgeCalculator:
2 | def __init__(self, birthday):
3 | self.year, self.month, self.day = (
4 | int(x) for x in birthday.split('-'))
5 |
6 | def calculate_age(self, date):
7 | year, month, day = (
8 | int(x) for x in date.split('-'))
9 | age = year - self.year
10 | if (month,day) < (self.month,self.day):
11 | age -= 1
12 | return age
13 |
14 | class DateAgeAdapter:
15 | def _str_date(self, date):
16 | return date.strftime("%Y-%m-%d")
17 |
18 | def __init__(self, birthday):
19 | birthday = self._str_date(birthday)
20 | self.calculator = AgeCalculator(birthday)
21 |
22 | def get_age(self, date):
23 | date = self._str_date(date)
24 | return self.calculator.calculate_age(date)
25 |
--------------------------------------------------------------------------------
/Chapter11/11_03_age_calculator_adapt_date.py:
--------------------------------------------------------------------------------
1 | class AgeCalculator:
2 | def __init__(self, birthday):
3 | self.year, self.month, self.day = (
4 | int(x) for x in birthday.split('-'))
5 |
6 | def calculate_age(self, date):
7 | year, month, day = (
8 | int(x) for x in date.split('-'))
9 | age = year - self.year
10 | if (month,day) < (self.month,self.day):
11 | age -= 1
12 | return age
13 |
14 | import datetime
15 | class AgeableDate(datetime.date):
16 | def split(self, char):
17 | return self.year, self.month, self.day
18 |
--------------------------------------------------------------------------------
/Chapter11/11_04_email_facade.py:
--------------------------------------------------------------------------------
1 | import smtplib
2 | import imaplib
3 |
4 | class EmailFacade:
5 | def __init__(self, host, username, password):
6 | self.host = host
7 | self.username = username
8 | self.password = password
9 |
10 | def send_email(self, to_email, subject, message):
11 | if not "@" in self.username:
12 | from_email = "{0}@{1}".format(
13 | self.username, self.host)
14 | else:
15 | from_email = self.username
16 | message = ("From: {0}\r\n"
17 | "To: {1}\r\n"
18 | "Subject: {2}\r\n\r\n{3}").format(
19 | from_email,
20 | to_email,
21 | subject,
22 | message)
23 |
24 | smtp = smtplib.SMTP(self.host)
25 | smtp.login(self.username, self.password)
26 | smtp.sendmail(from_email, [to_email], message)
27 |
28 | def get_inbox(self):
29 | mailbox = imaplib.IMAP4(self.host)
30 | mailbox.login(bytes(self.username, 'utf8'),
31 | bytes(self.password, 'utf8'))
32 | mailbox.select()
33 | x, data = mailbox.search(None, 'ALL')
34 | messages = []
35 | for num in data[0].split():
36 | x, message = mailbox.fetch(num, '(RFC822)')
37 | messages.append(message[0][1])
38 | return messages
39 |
--------------------------------------------------------------------------------
/Chapter11/11_05_flyweight_factory.py:
--------------------------------------------------------------------------------
1 | import weakref
2 |
3 | class CarModel:
4 | _models = weakref.WeakValueDictionary()
5 |
6 | def __new__(cls, model_name, *args, **kwargs):
7 | model = cls._models.get(model_name)
8 | if not model:
9 | model = super().__new__(cls)
10 | cls._models[model_name] = model
11 |
12 | return model
13 |
--------------------------------------------------------------------------------
/Chapter11/11_06_flyweight_init.py:
--------------------------------------------------------------------------------
1 | import weakref
2 |
3 | class CarModel:
4 | _models = weakref.WeakValueDictionary()
5 |
6 | def __new__(cls, model_name, *args, **kwargs):
7 | model = cls._models.get(model_name)
8 | if not model:
9 | model = super().__new__(cls)
10 | cls._models[model_name] = model
11 |
12 | return model
13 |
14 | def __init__(self, model_name, air=False, tilt=False,
15 | cruise_control=False, power_locks=False,
16 | alloy_wheels=False, usb_charger=False):
17 | if not hasattr(self, "initialized"):
18 | self.model_name = model_name
19 | self.air = air
20 | self.tilt = tilt
21 | self.cruise_control = cruise_control
22 | self.power_locks = power_locks
23 | self.alloy_wheels = alloy_wheels
24 | self.usb_charger = usb_charger
25 | self.initialized=True
26 |
--------------------------------------------------------------------------------
/Chapter11/11_07_flyweight_check_serial.py:
--------------------------------------------------------------------------------
1 | import weakref
2 |
3 | class CarModel:
4 | _models = weakref.WeakValueDictionary()
5 |
6 | def __new__(cls, model_name, *args, **kwargs):
7 | model = cls._models.get(model_name)
8 | if not model:
9 | model = super().__new__(cls)
10 | cls._models[model_name] = model
11 |
12 | return model
13 |
14 | def __init__(self, model_name, air=False, tilt=False,
15 | cruise_control=False, power_locks=False,
16 | alloy_wheels=False, usb_charger=False):
17 | if not hasattr(self, "initted"):
18 | self.model_name = model_name
19 | self.air = air
20 | self.tilt = tilt
21 | self.cruise_control = cruise_control
22 | self.power_locks = power_locks
23 | self.alloy_wheels = alloy_wheels
24 | self.usb_charger = usb_charger
25 | self.initted=True
26 |
27 | def check_serial(self, serial_number):
28 | print("Sorry, we are unable to check "
29 | "the serial number {0} on the {1} "
30 | "at this time".format(
31 | serial_number, self.model_name))
32 |
33 |
--------------------------------------------------------------------------------
/Chapter11/11_08_car_class.py:
--------------------------------------------------------------------------------
1 | import weakref
2 |
3 | class CarModel:
4 | _models = weakref.WeakValueDictionary()
5 |
6 | def __new__(cls, model_name, *args, **kwargs):
7 | model = cls._models.get(model_name)
8 | if not model:
9 | model = super().__new__(cls)
10 | cls._models[model_name] = model
11 |
12 | return model
13 |
14 | def __init__(self, model_name, air=False, tilt=False,
15 | cruise_control=False, power_locks=False,
16 | alloy_wheels=False, usb_charger=False):
17 | if not hasattr(self, "initted"):
18 | self.model_name = model_name
19 | self.air = air
20 | self.tilt = tilt
21 | self.cruise_control = cruise_control
22 | self.power_locks = power_locks
23 | self.alloy_wheels = alloy_wheels
24 | self.usb_charger = usb_charger
25 | self.initted=True
26 |
27 | def check_serial(self, serial_number):
28 | print("Sorry, we are unable to check "
29 | "the serial number {0} on the {1} "
30 | "at this time".format(
31 | serial_number, self.model_name))
32 |
33 | class Car:
34 | def __init__(self, model, color, serial):
35 | self.model = model
36 | self.color = color
37 | self.serial = serial
38 |
39 | def check_serial(self):
40 | return self.model.check_serial(self.serial)
41 |
--------------------------------------------------------------------------------
/Chapter11/11_09_window_commands.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | class Window:
4 | def exit(self):
5 | sys.exit(0)
6 |
7 | class Document:
8 | def __init__(self, filename):
9 | self.filename = filename
10 | self.contents = "This file cannot be modified"
11 |
12 | def save(self):
13 | with open(self.filename, 'w') as file:
14 | file.write(self.contents)
15 |
--------------------------------------------------------------------------------
/Chapter11/11_10_window_command_invokers.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | class Window:
4 | def exit(self):
5 | sys.exit(0)
6 |
7 | class Document:
8 | def __init__(self, filename):
9 | self.filename = filename
10 | self.contents = "This file cannot be modified"
11 |
12 | def save(self):
13 | with open(self.filename, 'w') as file:
14 | file.write(self.contents)
15 |
16 | class ToolbarButton:
17 | def __init__(self, name, iconname):
18 | self.name = name
19 | self.iconname = iconname
20 |
21 | def click(self):
22 | self.command.execute()
23 |
24 | class MenuItem:
25 | def __init__(self, menu_name, menuitem_name):
26 | self.menu = menu_name
27 | self.item = menuitem_name
28 |
29 | def click(self):
30 | self.command.execute()
31 |
32 | class KeyboardShortcut:
33 | def __init__(self, key, modifier):
34 | self.key = key
35 | self.modifier = modifier
36 |
37 | def keypress(self):
38 | self.command.execute()
39 |
--------------------------------------------------------------------------------
/Chapter11/11_11_window_command_commands.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | class Window:
4 | def exit(self):
5 | sys.exit(0)
6 |
7 | class Document:
8 | def __init__(self, filename):
9 | self.filename = filename
10 | self.contents = "This file cannot be modified"
11 |
12 | def save(self):
13 | with open(self.filename, 'w') as file:
14 | file.write(self.contents)
15 |
16 | class ToolbarButton:
17 | def __init__(self, name, iconname):
18 | self.name = name
19 | self.iconname = iconname
20 |
21 | def click(self):
22 | self.command.execute()
23 |
24 | class MenuItem:
25 | def __init__(self, menu_name, menuitem_name):
26 | self.menu = menu_name
27 | self.item = menuitem_name
28 |
29 | def click(self):
30 | self.command.execute()
31 |
32 | class KeyboardShortcut:
33 | def __init__(self, key, modifier):
34 | self.key = key
35 | self.modifier = modifier
36 |
37 | def keypress(self):
38 | self.command.execute()
39 |
40 | class SaveCommand:
41 | def __init__(self, document):
42 | self.document = document
43 |
44 | def execute(self):
45 | self.document.save()
46 |
47 | class ExitCommand:
48 | def __init__(self, window):
49 | self.window = window
50 |
51 | def execute(self):
52 | self.window.exit()
53 |
54 | window = Window()
55 | document = Document("a_document.txt")
56 | save = SaveCommand(document)
57 | exit = ExitCommand(window)
58 |
59 | save_button = ToolbarButton('save', 'save.png')
60 | save_button.command = save
61 | save_keystroke = KeyboardShortcut("s", "ctrl")
62 | save_keystroke.command = save
63 | exit_menu = MenuItem("File", "Exit")
64 | exit_menu.command = exit
65 |
66 |
--------------------------------------------------------------------------------
/Chapter11/11_12_window_command_function.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | class Window:
4 | def exit(self):
5 | sys.exit(0)
6 |
7 | class MenuItem:
8 | def click(self):
9 | self.command()
10 |
11 | window = Window()
12 | menu_item = MenuItem()
13 | menu_item.command = window.exit
14 |
--------------------------------------------------------------------------------
/Chapter11/11_13_document_command_callable.py:
--------------------------------------------------------------------------------
1 | class Document:
2 | def __init__(self, filename):
3 | self.filename = filename
4 | self.contents = "This file cannot be modified"
5 |
6 | def save(self):
7 | with open(self.filename, 'w') as file:
8 | file.write(self.contents)
9 |
10 | class KeyboardShortcut:
11 | def keypress(self):
12 | self.command()
13 |
14 | class SaveCommand:
15 | def __init__(self, document):
16 | self.document = document
17 |
18 | def __call__(self):
19 | self.document.save()
20 |
21 | document = Document("a_file.txt")
22 | shortcut = KeyboardShortcut()
23 | save_command = SaveCommand(document)
24 | shortcut.command = save_command
25 |
--------------------------------------------------------------------------------
/Chapter11/11_14_formatters.py:
--------------------------------------------------------------------------------
1 | class FranceDateFormatter:
2 | def format_date(self, y, m, d):
3 | y, m, d = (str(x) for x in (y,m,d))
4 | y = '20' + y if len(y) == 2 else y
5 | m = '0' + m if len(m) == 1 else m
6 | d = '0' + d if len(d) == 1 else d
7 | return("{0}/{1}/{2}".format(d,m,y))
8 |
9 | class USADateFormatter:
10 | def format_date(self, y, m, d):
11 | y, m, d = (str(x) for x in (y,m,d))
12 | y = '20' + y if len(y) == 2 else y
13 | m = '0' + m if len(m) == 1 else m
14 | d = '0' + d if len(d) == 1 else d
15 | return("{0}-{1}-{2}".format(m,d,y))
16 |
17 | class FranceCurrencyFormatter:
18 | def format_currency(self, base, cents):
19 | base, cents = (str(x) for x in (base, cents))
20 | if len(cents) == 0:
21 | cents = '00'
22 | elif len(cents) == 1:
23 | cents = '0' + cents
24 |
25 | digits = []
26 | for i,c in enumerate(reversed(base)):
27 | if i and not i % 3:
28 | digits.append(' ')
29 | digits.append(c)
30 | base = ''.join(reversed(digits))
31 | return "{0}€{1}".format(base, cents)
32 |
33 | class USACurrencyFormatter:
34 | def format_currency(self, base, cents):
35 | base, cents = (str(x) for x in (base, cents))
36 | if len(cents) == 0:
37 | cents = '00'
38 | elif len(cents) == 1:
39 | cents = '0' + cents
40 |
41 | digits = []
42 | for i,c in enumerate(reversed(base)):
43 | if i and not i % 3:
44 | digits.append(',')
45 | digits.append(c)
46 | base = ''.join(reversed(digits))
47 | return "${0}.{1}".format(base, cents)
48 |
49 |
--------------------------------------------------------------------------------
/Chapter11/11_15_formatter_factories.py:
--------------------------------------------------------------------------------
1 | class FranceDateFormatter:
2 | def format_date(self, y, m, d):
3 | y, m, d = (str(x) for x in (y,m,d))
4 | y = '20' + y if len(y) == 2 else y
5 | m = '0' + m if len(m) == 1 else m
6 | d = '0' + d if len(d) == 1 else d
7 | return("{0}/{1}/{2}".format(d,m,y))
8 |
9 | class USADateFormatter:
10 | def format_date(self, y, m, d):
11 | y, m, d = (str(x) for x in (y,m,d))
12 | y = '20' + y if len(y) == 2 else y
13 | m = '0' + m if len(m) == 1 else m
14 | d = '0' + d if len(d) == 1 else d
15 | return("{0}-{1}-{2}".format(m,d,y))
16 |
17 | class FranceCurrencyFormatter:
18 | def format_currency(self, base, cents):
19 | base, cents = (str(x) for x in (base, cents))
20 | if len(cents) == 0:
21 | cents = '00'
22 | elif len(cents) == 1:
23 | cents = '0' + cents
24 |
25 | digits = []
26 | for i,c in enumerate(reversed(base)):
27 | if i and not i % 3:
28 | digits.append(' ')
29 | digits.append(c)
30 | base = ''.join(reversed(digits))
31 | return "{0}€{1}".format(base, cents)
32 |
33 | class USACurrencyFormatter:
34 | def format_currency(self, base, cents):
35 | base, cents = (str(x) for x in (base, cents))
36 | if len(cents) == 0:
37 | cents = '00'
38 | elif len(cents) == 1:
39 | cents = '0' + cents
40 |
41 | digits = []
42 | for i,c in enumerate(reversed(base)):
43 | if i and not i % 3:
44 | digits.append(',')
45 | digits.append(c)
46 | base = ''.join(reversed(digits))
47 | return "${0}.{1}".format(base, cents)
48 |
49 | class USAFormatterFactory:
50 | def create_date_formatter(self):
51 | return USADateFormatter()
52 | def create_currency_formatter(self):
53 | return USACurrencyFormatter()
54 |
55 | class FranceFormatterFactory:
56 | def create_date_formatter(self):
57 | return FranceDateFormatter()
58 | def create_currency_formatter(self):
59 | return FranceCurrencyFormatter()
60 |
61 | country_code = "US"
62 | factory_map = {
63 | "US": USAFormatterFactory,
64 | "FR": FranceFormatterFactory}
65 | formatter_factory = factory_map.get(country_code)()
66 |
--------------------------------------------------------------------------------
/Chapter11/11_16_composite_folder_methods.py:
--------------------------------------------------------------------------------
1 | class Folder:
2 | def __init__(self, name):
3 | self.name = name
4 | self.children = {}
5 |
6 | def add_child(self, child):
7 | pass
8 |
9 | def move(self, new_path):
10 | pass
11 |
12 | def copy(self, new_path):
13 | pass
14 |
15 | def delete(self):
16 | pass
17 |
18 | class File:
19 | def __init__(self, name, contents):
20 | self.name = name
21 | self.contents = contents
22 |
23 | def move(self, new_path):
24 | pass
25 |
26 | def copy(self, new_path):
27 | pass
28 |
29 | def delete(self):
30 | pass
31 |
--------------------------------------------------------------------------------
/Chapter11/11_17_component_hierarchy.py:
--------------------------------------------------------------------------------
1 | class Component:
2 | def __init__(self, name):
3 | self.name = name
4 |
5 | def move(self, new_path):
6 | new_folder =get_path(new_path)
7 | del self.parent.children[self.name]
8 | new_folder.children[self.name] = self
9 | self.parent = new_folder
10 |
11 | def delete(self):
12 | del self.parent.children[self.name]
13 |
14 | class Folder(Component):
15 | def __init__(self, name):
16 | super().__init__(name)
17 | self.children = {}
18 |
19 | def add_child(self, child):
20 | pass
21 |
22 | def copy(self, new_path):
23 | pass
24 |
25 | class File(Component):
26 | def __init__(self, name, contents):
27 | super().__init__(name)
28 | self.contents = contents
29 |
30 | def copy(self, new_path):
31 | pass
32 |
33 | root = Folder('')
34 | def get_path(path):
35 | names = path.split('/')[1:]
36 | node = root
37 | for name in names:
38 | node = node.children[name]
39 | return node
40 |
41 |
--------------------------------------------------------------------------------
/Chapter11/11_18_add_child.py:
--------------------------------------------------------------------------------
1 | class Component:
2 | def __init__(self, name):
3 | self.name = name
4 |
5 | def move(self, new_path):
6 | new_folder =get_path(new_path)
7 | del self.parent.children[self.name]
8 | new_folder.children[self.name] = self
9 | self.parent = new_folder
10 |
11 | def delete(self):
12 | del self.parent.children[self.name]
13 |
14 | class Folder(Component):
15 | def __init__(self, name):
16 | super().__init__(name)
17 | self.children = {}
18 |
19 | def add_child(self, child):
20 | child.parent = self
21 | self.children[child.name] = child
22 |
23 | def copy(self, new_path):
24 | pass
25 |
26 | class File(Component):
27 | def __init__(self, name, contents):
28 | super().__init__(name)
29 | self.contents = contents
30 |
31 | def copy(self, new_path):
32 | pass
33 |
34 | root = Folder('')
35 | def get_path(path):
36 | names = path.split('/')[1:]
37 | node = root
38 | for name in names:
39 | node = node.children[name]
40 | return node
41 |
42 |
--------------------------------------------------------------------------------
/Chapter12/12_01_simplest_unittest.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | class CheckNumbers(unittest.TestCase):
4 | def test_int_float(self):
5 | self.assertEqual(1, 1.0)
6 |
7 | def test_str_float(self):
8 | self.assertEqual(1, "1")
9 |
10 | if __name__ == "__main__":
11 | unittest.main()
12 |
--------------------------------------------------------------------------------
/Chapter12/12_02_assertraises_python.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | def average(seq):
4 | return sum(seq) / len(seq)
5 |
6 | class TestAverage(unittest.TestCase):
7 | def test_zero(self):
8 | self.assertRaises(ZeroDivisionError,
9 | average,
10 | [])
11 |
12 | def test_with_zero(self):
13 | with self.assertRaises(ZeroDivisionError):
14 | average([])
15 |
16 | if __name__ == "__main__":
17 | unittest.main()
18 |
--------------------------------------------------------------------------------
/Chapter12/12_03_stats.py:
--------------------------------------------------------------------------------
1 | from collections import defaultdict
2 |
3 | class StatsList(list):
4 | def mean(self):
5 | return sum(self) / len(self)
6 |
7 | def median(self):
8 | if len(self) % 2:
9 | return self[int(len(self) / 2)]
10 | else:
11 | idx = int(len(self) / 2)
12 | return (self[idx] + self[idx-1]) / 2
13 |
14 | def mode(self):
15 | freqs = defaultdict(int)
16 | for item in self:
17 | freqs[item] += 1
18 | mode_freq = max(freqs.values())
19 | modes = []
20 | for item, value in freqs.items():
21 | if value == mode_freq:
22 | modes.append(item)
23 | return modes
24 |
--------------------------------------------------------------------------------
/Chapter12/12_04_test_stats.py:
--------------------------------------------------------------------------------
1 | from stats import StatsList
2 | import unittest
3 |
4 | class TestValidInputs(unittest.TestCase):
5 | def setUp(self):
6 | self.stats = StatsList([1,2,2,3,3,4])
7 |
8 | def test_mean(self):
9 | self.assertEqual(self.stats.mean(), 2.5)
10 |
11 | def test_median(self):
12 | self.assertEqual(self.stats.median(), 2.5)
13 | self.stats.append(4)
14 | self.assertEqual(self.stats.median(), 3)
15 |
16 | def test_mode(self):
17 | self.assertEqual(self.stats.mode(), [2,3])
18 | self.stats.remove(2)
19 | self.assertEqual(self.stats.mode(), [3])
20 |
21 | if __name__ == "__main__":
22 | unittest.main()
23 |
--------------------------------------------------------------------------------
/Chapter12/12_05_skipping_tests.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import sys
3 |
4 | class SkipTests(unittest.TestCase):
5 | @unittest.expectedFailure
6 | def test_fails(self):
7 | self.assertEqual(False, True)
8 |
9 | @unittest.skip("Test is useless")
10 | def test_skip(self):
11 | self.assertEqual(False, True)
12 |
13 | @unittest.skipIf(sys.version_info.minor == 4,
14 | "broken on 3.4")
15 | def test_skipif(self):
16 | self.assertEqual(False, True)
17 |
18 | @unittest.skipUnless(sys.platform.startswith('linux'),
19 | "broken unless on linux")
20 | def test_skipunless(self):
21 | self.assertEqual(False, True)
22 |
23 | if __name__ == "__main__":
24 | unittest.main()
25 |
--------------------------------------------------------------------------------
/Chapter12/12_06_simplestpytest.py:
--------------------------------------------------------------------------------
1 | def test_int_float():
2 | assert 1 == 1.0
3 |
--------------------------------------------------------------------------------
/Chapter12/12_06_simplestpytest.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/12_06_simplestpytest.pyc
--------------------------------------------------------------------------------
/Chapter12/12_07_class_pytest.py:
--------------------------------------------------------------------------------
1 |
2 | class TestNumbers:
3 | def test_int_float(self):
4 | assert 1 == 1.0
5 |
6 | def test_int_str(self):
7 | assert 1 == "1"
8 |
--------------------------------------------------------------------------------
/Chapter12/12_07_class_pytest.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/12_07_class_pytest.pyc
--------------------------------------------------------------------------------
/Chapter12/12_08_setup_teardown.py:
--------------------------------------------------------------------------------
1 | def setup_module(module):
2 | print("setting up MODULE {0}".format(
3 | module.__name__))
4 |
5 | def teardown_module(module):
6 | print("tearing down MODULE {0}".format(
7 | module.__name__))
8 |
9 | def test_a_function():
10 | print("RUNNING TEST FUNCTION")
11 |
12 | class BaseTest:
13 | def setup_class(cls):
14 | print("setting up CLASS {0}".format(
15 | cls.__name__))
16 |
17 | def teardown_class(cls):
18 | print("tearing down CLASS {0}\n".format(
19 | cls.__name__))
20 |
21 | def setup_method(self, method):
22 | print("setting up METHOD {0}".format(
23 | method.__name__))
24 |
25 | def teardown_method(self, method):
26 | print("tearing down METHOD {0}".format(
27 | method.__name__))
28 |
29 | class TestClass1(BaseTest):
30 | def test_method_1(self):
31 | print("RUNNING METHOD 1-1")
32 |
33 | def test_method_2(self):
34 | print("RUNNING METHOD 1-2")
35 |
36 | class TestClass2(BaseTest):
37 | def test_method_1(self):
38 | print("RUNNING METHOD 2-1")
39 |
40 | def test_method_2(self):
41 | print("RUNNING METHOD 2-2")
42 |
--------------------------------------------------------------------------------
/Chapter12/12_08_setup_teardown.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/12_08_setup_teardown.pyc
--------------------------------------------------------------------------------
/Chapter12/12_09_funcargs.py:
--------------------------------------------------------------------------------
1 | from stats import StatsList
2 |
3 | def pytest_funcarg__valid_stats(request):
4 | return StatsList([1,2,2,3,3,4])
5 |
6 |
7 | def test_mean(valid_stats):
8 | assert valid_stats.mean() == 2.5
9 |
10 | def test_median(valid_stats):
11 | assert valid_stats.median() == 2.5
12 | valid_stats.append(4)
13 | assert valid_stats.median() == 3
14 |
15 | def test_mode(valid_stats):
16 | assert valid_stats.mode() == [2,3]
17 | valid_stats.remove(2)
18 | assert valid_stats.mode() == [3]
19 |
--------------------------------------------------------------------------------
/Chapter12/12_09_funcargs.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/12_09_funcargs.pyc
--------------------------------------------------------------------------------
/Chapter12/12_10_funcarg_finalizer.py:
--------------------------------------------------------------------------------
1 | import tempfile
2 | import shutil
3 | import os.path
4 |
5 | def pytest_funcarg__temp_dir(request):
6 | dir = tempfile.mkdtemp()
7 | print(dir)
8 |
9 | def cleanup():
10 | shutil.rmtree(dir)
11 | request.addfinalizer(cleanup)
12 | return dir
13 |
14 | def test_osfiles(temp_dir):
15 | os.mkdir(os.path.join(temp_dir, 'a'))
16 | os.mkdir(os.path.join(temp_dir, 'b'))
17 | dir_contents = os.listdir(temp_dir)
18 | assert len(dir_contents) == 2
19 | assert 'a' in dir_contents
20 | assert 'b' in dir_contents
21 |
--------------------------------------------------------------------------------
/Chapter12/12_10_funcarg_finalizer.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/12_10_funcarg_finalizer.pyc
--------------------------------------------------------------------------------
/Chapter12/12_11_echo_server.py:
--------------------------------------------------------------------------------
1 | import socket
2 |
3 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
4 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
5 | s.bind(('localhost',1028))
6 | s.listen(1)
7 |
8 | try:
9 | while True:
10 | client, address = s.accept()
11 | data = client.recv(1024)
12 | client.send(data)
13 | client.close()
14 | finally:
15 | s.close()
16 |
--------------------------------------------------------------------------------
/Chapter12/12_12_pytest_echo.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | import socket
3 | import time
4 |
5 | def pytest_funcarg__echoserver(request):
6 | def setup():
7 | p = subprocess.Popen(
8 | ['python3', '12_11_echo_server.py'])
9 | time.sleep(1)
10 | return p
11 |
12 | def cleanup(p):
13 | p.terminate()
14 |
15 | return request.cached_setup(
16 | setup=setup,
17 | teardown=cleanup,
18 | scope="session")
19 |
20 | def pytest_funcarg__clientsocket(request):
21 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
22 | s.connect(('localhost', 1028))
23 | request.addfinalizer(lambda: s.close())
24 | return s
25 |
26 | def test_echo(echoserver, clientsocket):
27 | clientsocket.send(b"abc")
28 | assert clientsocket.recv(3) == b'abc'
29 |
30 | def test_echo2(echoserver, clientsocket):
31 | clientsocket.send(b"def")
32 | assert clientsocket.recv(3) == b'def'
33 |
--------------------------------------------------------------------------------
/Chapter12/12_12_pytest_echo.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/12_12_pytest_echo.pyc
--------------------------------------------------------------------------------
/Chapter12/12_13_pytest_simple_skip.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import py.test
3 |
4 | def test_simple_skip():
5 | if sys.platform != "fakeos":
6 | py.test.skip("Test works only on fakeOS")
7 |
8 | fakeos.do_something_fake()
9 | assert fakeos.did_not_happen
10 |
--------------------------------------------------------------------------------
/Chapter12/12_13_pytest_simple_skip.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/12_13_pytest_simple_skip.pyc
--------------------------------------------------------------------------------
/Chapter12/12_14_pytest_importorskip.py:
--------------------------------------------------------------------------------
1 | import py.test
2 |
3 | py.test.importorskip("postgresql")
4 |
5 | def test_connect():
6 | pass
7 |
8 | def test_query():
9 | pass
10 |
--------------------------------------------------------------------------------
/Chapter12/12_14_pytest_importorskip.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/12_14_pytest_importorskip.pyc
--------------------------------------------------------------------------------
/Chapter12/12_15_pytest_skipifmark.py:
--------------------------------------------------------------------------------
1 | import py.test
2 |
3 | @py.test.mark.skipif("sys.version_info <= (3,0)")
4 | def test_python3():
5 | assert b"hello".decode() == "hello"
6 |
--------------------------------------------------------------------------------
/Chapter12/12_15_pytest_skipifmark.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/12_15_pytest_skipifmark.pyc
--------------------------------------------------------------------------------
/Chapter12/12_16_mock_flightstatus.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import redis
3 |
4 | class FlightStatusTracker:
5 | ALLOWED_STATUSES = {'CANCELLED', 'DELAYED', 'ON TIME'}
6 |
7 | def __init__(self):
8 | self.redis = redis.StrictRedis()
9 |
10 | def change_status(self, flight, status):
11 | status = status.upper()
12 | if status not in self.ALLOWED_STATUSES:
13 | raise ValueError(
14 | "{} is not a valid status".format(status)
15 | )
16 |
17 | key = "flightno:{}".format(flight)
18 | value = "{}|{}".format(datetime.datetime.now().isoformat(), status)
19 | self.redis.set(key, value)
20 |
21 | def last_status(self, flight, status):
22 | key = "flightno:{}".format(flight)
23 | timestamp, status = self.redis.get(key).split('|')
24 | return timestamp, status
25 |
26 |
27 |
28 | from unittest.mock import Mock
29 | import py.test
30 | def pytest_funcarg__tracker():
31 | return FlightStatusTracker()
32 |
33 | def test_mock_method(tracker):
34 | tracker.redis.set = Mock()
35 | with py.test.raises(ValueError) as ex:
36 | tracker.change_status("AC101", "lost")
37 | assert ex.value.args[0] == "LOST is not a valid status"
38 | assert tracker.redis.set.call_count == 0
39 |
40 | from unittest.mock import patch
41 | def test_patch(tracker):
42 | tracker.redis.set = Mock()
43 | fake_now = datetime.datetime(2015, 4, 1)
44 | with patch('datetime.datetime') as dt:
45 | dt.now.return_value = fake_now
46 | tracker.change_status("AC102", "on time")
47 | dt.now.assert_called_once_with()
48 | tracker.redis.set.assert_called_once_with(
49 | "flightno:AC102",
50 | "2015-04-01T00:00:00|ON TIME"
51 | )
52 |
53 |
--------------------------------------------------------------------------------
/Chapter12/12_17_mock_redis.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import redis
3 |
4 | class FlightStatusTracker:
5 | ALLOWED_STATUSES = {'CANCELLED', 'DELAYED', 'ON TIME', 'ARRIVED'}
6 |
7 |
8 | def __init__(self, redis_instance=None):
9 | self.redis = redis_instance if redis_instance else redis.StrictRedis()
10 |
11 |
12 |
13 | def change_status(self, flight, status):
14 | status = status.upper()
15 | if status not in self.ALLOWED_STATUSES:
16 | raise ValueError(
17 | "{} is not a valid status".format(status)
18 | )
19 |
20 | key = "flightno:{}".format(flight)
21 | value = "{}|{}".format(datetime.datetime.now().isoformat(), status)
22 | self.redis.set(key, value)
23 |
--------------------------------------------------------------------------------
/Chapter12/12_30_coverage_unittest.py:
--------------------------------------------------------------------------------
1 | from stats import StatsList
2 | import unittest
3 |
4 | class TestMean(unittest.TestCase):
5 | def test_mean(self):
6 | self.assertEqual(StatsList([1,2,2,3,3,4]).mean(), 2.5)
7 |
8 | if __name__ == "__main__":
9 | unittest.main()
10 |
--------------------------------------------------------------------------------
/Chapter12/__pycache__/stats.cpython-34.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/__pycache__/stats.cpython-34.pyc
--------------------------------------------------------------------------------
/Chapter12/__pycache__/test_mock.cpython-34-PYTEST.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/__pycache__/test_mock.cpython-34-PYTEST.pyc
--------------------------------------------------------------------------------
/Chapter12/casestudy/__pycache__/test_vigenere_cipher.cpython-34-PYTEST.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/casestudy/__pycache__/test_vigenere_cipher.cpython-34-PYTEST.pyc
--------------------------------------------------------------------------------
/Chapter12/casestudy/__pycache__/vigenere_cipher.cpython-34.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/casestudy/__pycache__/vigenere_cipher.cpython-34.pyc
--------------------------------------------------------------------------------
/Chapter12/casestudy/test_vigenere_cipher.py:
--------------------------------------------------------------------------------
1 | from vigenere_cipher import VigenereCipher
2 | from vigenere_cipher import combine_character
3 | from vigenere_cipher import separate_character
4 |
5 | def test_encode():
6 | cipher = VigenereCipher("TRAIN")
7 | encoded = cipher.encode("ENCODEDINPYTHON")
8 | assert encoded == "XECWQXUIVCRKHWA"
9 |
10 | def test_encode_character():
11 | cipher = VigenereCipher("TRAIN")
12 | encoded = cipher.encode("E")
13 | assert encoded == "X"
14 |
15 | def test_encode_spaces():
16 | cipher = VigenereCipher("TRAIN")
17 | encoded = cipher.encode("ENCODED IN PYTHON")
18 | assert encoded == "XECWQXUIVCRKHWA"
19 |
20 | def test_encode_lowercase():
21 | cipher = VigenereCipher("TRain")
22 | encoded = cipher.encode("encoded in Python")
23 | assert encoded == "XECWQXUIVCRKHWA"
24 |
25 | def test_combine_character():
26 | assert combine_character("E", "T") == "X"
27 | assert combine_character("N", "R") == "E"
28 |
29 | def test_extend_keyword():
30 | cipher = VigenereCipher("TRAIN")
31 | extended = cipher.extend_keyword(16)
32 | assert extended == "TRAINTRAINTRAINT"
33 |
34 | def test_extend_keyword_even():
35 | cipher = VigenereCipher("TRAIN")
36 | extended = cipher.extend_keyword(15)
37 | assert extended == "TRAINTRAINTRAIN"
38 |
39 | def test_separate_character():
40 | assert separate_character("X", "T") == "E"
41 | assert separate_character("E", "R") == "N"
42 |
43 | def test_decode():
44 | cipher = VigenereCipher("TRAIN")
45 | decoded = cipher.decode("XECWQXUIVCRKHWA")
46 | assert decoded == "ENCODEDINPYTHON"
47 |
--------------------------------------------------------------------------------
/Chapter12/casestudy/test_vigenere_cipher.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/casestudy/test_vigenere_cipher.pyc
--------------------------------------------------------------------------------
/Chapter12/casestudy/vigenere_cipher.py:
--------------------------------------------------------------------------------
1 | def combine_character(plain, keyword):
2 | plain = plain.upper()
3 | keyword = keyword.upper()
4 | plain_num = ord(plain) - ord('A')
5 | keyword_num = ord(keyword) - ord('A')
6 | return chr(ord('A') + (plain_num + keyword_num) % 26)
7 |
8 | def separate_character(cypher, keyword):
9 | cypher = cypher.upper()
10 | keyword = keyword.upper()
11 | cypher_num = ord(cypher) - ord('A')
12 | keyword_num = ord(keyword) - ord('A')
13 | return chr(ord('A') + (cypher_num - keyword_num) % 26)
14 |
15 | class VigenereCipher:
16 | def __init__(self, keyword):
17 | self.keyword = keyword
18 |
19 | def extend_keyword(self, number):
20 | repeats = number // len(self.keyword) + 1
21 | return (self.keyword * repeats)[:number]
22 |
23 | def _code(self, text, combine_func):
24 | text = text.replace(" ", "").upper()
25 | combined = []
26 | keyword = self.extend_keyword(len(text))
27 | for p,k in zip(text, keyword):
28 | combined.append(combine_func(p,k))
29 | return "".join(combined)
30 |
31 | def encode(self, plaintext):
32 | return self._code(plaintext, combine_character)
33 |
34 | def decode(self, ciphertext):
35 | return self._code(ciphertext, separate_character)
36 |
--------------------------------------------------------------------------------
/Chapter12/casestudy/vigenere_cipher.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/casestudy/vigenere_cipher.pyc
--------------------------------------------------------------------------------
/Chapter12/casestudy/vigenere_cipher1.py:
--------------------------------------------------------------------------------
1 | def combine_character(plain, keyword):
2 | plain = plain.upper()
3 | keyword = keyword.upper()
4 | plain_num = ord(plain) - ord('A')
5 | keyword_num = ord(keyword) - ord('A')
6 | return chr(ord('A') + (plain_num + keyword_num) % 26)
7 |
8 | def separate_character(cypher, keyword):
9 | cypher = cypher.upper()
10 | keyword = keyword.upper()
11 | cypher_num = ord(cypher) - ord('A')
12 | keyword_num = ord(keyword) - ord('A')
13 | return chr(ord('A') + (cypher_num - keyword_num) % 26)
14 |
15 | class VigenereCipher:
16 | def __init__(self, keyword):
17 | self.keyword = keyword
18 |
19 | def extend_keyword(self, number):
20 | repeats = number // len(self.keyword) + 1
21 | return (self.keyword * repeats)[:number]
22 |
23 | def encode(self, plaintext):
24 | plaintext = plaintext.replace(" ", "").upper()
25 | cipher = []
26 | keyword = self.extend_keyword(len(plaintext))
27 | for p,k in zip(plaintext, keyword):
28 | cipher.append(combine_character(p,k))
29 | return "".join(cipher)
30 |
31 | def decode(self, ciphertext):
32 | plain = []
33 | keyword = self.extend_keyword(len(ciphertext))
34 | for p,k in zip(ciphertext, keyword):
35 | plain.append(separate_character(p,k))
36 | return "".join(plain)
37 |
--------------------------------------------------------------------------------
/Chapter12/dump.rdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/dump.rdb
--------------------------------------------------------------------------------
/Chapter12/stats.py:
--------------------------------------------------------------------------------
1 | from collections import defaultdict
2 |
3 | class StatsList(list):
4 | def mean(self):
5 | return sum(self) / len(self)
6 |
7 | def median(self):
8 | if len(self) % 2:
9 | return self[int(len(self) / 2)]
10 | else:
11 | idx = int(len(self) / 2)
12 | return (self[idx] + self[idx-1]) / 2
13 |
14 | def mode(self):
15 | freqs = defaultdict(int)
16 | for item in self:
17 | freqs[item] += 1
18 | mode_freq = max(freqs.values())
19 | modes = []
20 | for item, value in freqs.items():
21 | if value == mode_freq:
22 | modes.append(item)
23 | return modes
24 |
--------------------------------------------------------------------------------
/Chapter12/stats.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter12/stats.pyc
--------------------------------------------------------------------------------
/Chapter12/test_mock.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import redis
3 |
4 | class FlightStatusTracker:
5 | ALLOWED_STATUSES = {'CANCELLED', 'DELAYED', 'ON TIME', 'ARRIVED'}
6 |
7 | def __init__(self, redis_instance=None):
8 | self.redis = if redis_instance else redis.StrictRedis()
9 |
10 | def change_status(self, flight, status):
11 | status = status.upper()
12 | if status not in self.ALLOWED_STATUSES:
13 | raise ValueError(
14 | "{} is not a valid status".format(status)
15 | )
16 |
17 | key = "flightno:{}".format(flight)
18 | value = "{}|{}".format(datetime.datetime.now().isoformat(), status)
19 | self.redis.set(key, value)
20 |
--------------------------------------------------------------------------------
/Chapter13/13_01_two_basic_threads.py:
--------------------------------------------------------------------------------
1 | from threading import Thread
2 |
3 | class InputReader(Thread):
4 | def run(self):
5 | self.line_of_text = input()
6 |
7 | print("Enter some text and press enter: ")
8 | thread = InputReader()
9 | thread.start()
10 |
11 | count = result = 1
12 | while thread.is_alive():
13 | result = count * count
14 | count += 1
15 |
16 | print("calculated squares up to {0} * {0} = {1}".format(
17 | count, result))
18 | print("while you typed '{}'".format(thread.line_of_text))
19 |
--------------------------------------------------------------------------------
/Chapter13/13_02_thread_wait.py:
--------------------------------------------------------------------------------
1 | from threading import Thread
2 | import json
3 | from urllib.request import urlopen
4 | import time
5 |
6 | CITIES = [
7 | 'Edmonton', 'Victoria', 'Winnipeg', 'Fredericton',
8 | "St. John's", 'Halifax', 'Toronto', 'Charlottetown',
9 | 'Quebec City', 'Regina'
10 | ]
11 |
12 | class TempGetter(Thread):
13 | def __init__(self, city):
14 | super().__init__()
15 | self.city = city
16 |
17 | def run(self):
18 | url_template = (
19 | 'http://api.openweathermap.org/data/2.5/'
20 | 'weather?q={},CA&units=metric')
21 | response = urlopen(url_template.format(self.city))
22 | data = json.loads(response.read().decode())
23 | self.temperature = data['main']['temp']
24 |
25 | threads = [TempGetter(c) for c in CITIES]
26 | start = time.time()
27 | for thread in threads:
28 | thread.start()
29 |
30 | for thread in threads:
31 | thread.join()
32 |
33 | for thread in threads:
34 | print(
35 | "it is {0.temperature:.0f}°C in {0.city}".format(thread))
36 | print(
37 | "Got {} temps in {} seconds".format(
38 | len(threads), time.time() - start))
39 |
--------------------------------------------------------------------------------
/Chapter13/13_03_parallel.py:
--------------------------------------------------------------------------------
1 | from multiprocessing import Process, cpu_count
2 | from threading import Thread
3 | import time
4 | import os
5 |
6 | class MuchCPU(Process):
7 | def run(self):
8 | print(os.getpid())
9 | for i in range(200000000):
10 | pass
11 |
12 | if __name__ == '__main__':
13 | procs = [MuchCPU() for f in range(cpu_count())]
14 |
15 | t = time.time()
16 | for p in procs:
17 | p.start()
18 | for p in procs:
19 | p.join()
20 | print('work took {} seconds'.format(time.time() - t))
21 |
--------------------------------------------------------------------------------
/Chapter13/13_04_prime_factor.py:
--------------------------------------------------------------------------------
1 | import random
2 | from multiprocessing.pool import Pool
3 |
4 | def prime_factor(value):
5 | factors = []
6 | for divisor in range(2, value-1):
7 | quotient, remainder = divmod(value, divisor)
8 | if not remainder:
9 | factors.extend(prime_factor(divisor))
10 | factors.extend(prime_factor(quotient))
11 | break
12 | else:
13 | factors = [value]
14 | return factors
15 |
16 | if __name__ == '__main__':
17 | pool = Pool()
18 |
19 | to_factor = [
20 | random.randint(100000, 50000000) for i in range(20)
21 | ]
22 | results = pool.map(prime_factor, to_factor)
23 | for value, factors in zip(to_factor, results):
24 | print("The factors of {} are {}".format(value, factors))
25 |
--------------------------------------------------------------------------------
/Chapter13/13_05_post_search.py:
--------------------------------------------------------------------------------
1 | def search(paths, query_q, results_q):
2 | lines = []
3 | for path in paths:
4 | lines.extend(l.strip() for l in path.open())
5 |
6 | query = query_q.get()
7 | while query:
8 | results_q.put([l for l in lines if query in l])
9 | query = query_q.get()
10 |
11 | if __name__ == '__main__':
12 | from multiprocessing import Process, Queue, cpu_count
13 | from path import path
14 | cpus = cpu_count()
15 | pathnames = [f for f in path('.').listdir() if f.isfile()]
16 | paths = [pathnames[i::cpus] for i in range(cpus)]
17 | query_queues = [Queue() for p in range(cpus)]
18 | results_queue = Queue()
19 |
20 | search_procs = [
21 | Process(target=search, args=(p, q, results_queue))
22 | for p, q in zip(paths, query_queues)
23 | ]
24 | for proc in search_procs: proc.start()
25 |
26 | for q in query_queues:
27 | q.put("def")
28 | q.put(None) # Signal process termination
29 |
30 | for i in range(cpus):
31 | for match in results_queue.get():
32 | print(match)
33 | for proc in search_procs: proc.join()
34 |
--------------------------------------------------------------------------------
/Chapter13/13_06_futures.py:
--------------------------------------------------------------------------------
1 | from concurrent.futures import ThreadPoolExecutor
2 | from pathlib import Path
3 | from os.path import sep as pathsep
4 | from collections import deque
5 |
6 | def find_files(path, query_string):
7 | subdirs = []
8 | for p in path.iterdir():
9 | full_path = str(p.absolute())
10 | if p.is_dir() and not p.is_symlink():
11 | subdirs.append(p)
12 | if query_string in full_path:
13 | print(full_path)
14 |
15 | return subdirs
16 |
17 | query = '.py'
18 | futures = deque()
19 | basedir = Path(pathsep).absolute()
20 |
21 | with ThreadPoolExecutor(max_workers=10) as executor:
22 | futures.append(
23 | executor.submit(find_files, basedir, query))
24 | while futures:
25 | future = futures.popleft()
26 | if future.exception():
27 | continue
28 | elif future.done():
29 | subdirs = future.result()
30 | for subdir in subdirs:
31 | futures.append(
32 | executor.submit(find_files, subdir, query))
33 | else:
34 | futures.append(future)
35 |
--------------------------------------------------------------------------------
/Chapter13/13_07_basic_async.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import random
3 |
4 | @asyncio.coroutine
5 | def random_sleep(counter):
6 | delay = random.random() * 5
7 | print("{} sleeps for {:.2f} seconds".format(counter, delay))
8 | yield from asyncio.sleep(delay)
9 | print("{} awakens".format(counter))
10 |
11 | @asyncio.coroutine
12 | def five_sleepers():
13 | print("Creating five tasks")
14 | tasks = [asyncio.async(random_sleep(i)) for i in range(5)]
15 | print("Sleeping after starting five tasks")
16 | yield from asyncio.sleep(2)
17 | print("Waking and waiting for five tasks")
18 | yield from asyncio.wait(tasks)
19 |
20 | asyncio.get_event_loop().run_until_complete(five_sleepers())
21 | print("Done five tasks")
22 |
--------------------------------------------------------------------------------
/Chapter13/13_08_asyncio_dns.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from contextlib import suppress
3 |
4 | ip_map = {
5 | b'facebook.com.': '173.252.120.6',
6 | b'yougov.com.': '213.52.133.246',
7 | b'wipo.int.': '193.5.93.80'
8 | }
9 |
10 | def lookup_dns(data):
11 | domain = b''
12 | pointer, part_length = 13, data[12]
13 | while part_length:
14 | domain += data[pointer:pointer+part_length] + b'.'
15 | pointer += part_length + 1
16 | part_length = data[pointer - 1]
17 |
18 | ip = ip_map.get(domain, '127.0.0.1')
19 |
20 | return domain, ip
21 |
22 | def create_response(data, ip):
23 | ba = bytearray
24 | packet = ba(data[:2]) + ba([129, 128]) + data[4:6] * 2
25 | packet += ba(4) + data[12:]
26 | packet += ba([192, 12, 0, 1, 0, 1, 0, 0, 0, 60, 0, 4])
27 | for x in ip.split('.'): packet.append(int(x))
28 | return packet
29 |
30 | class DNSProtocol(asyncio.DatagramProtocol):
31 | def connection_made(self, transport):
32 | self.transport = transport
33 |
34 | def datagram_received(self, data, addr):
35 | print("Received request from {}".format(addr[0]))
36 | domain, ip = lookup_dns(data)
37 | print("Sending IP {} for {} to {}".format(
38 | domain.decode(), ip, addr[0]))
39 | self.transport.sendto(create_response(data, ip), addr)
40 |
41 |
42 | loop = asyncio.get_event_loop()
43 | transport, protocol = loop.run_until_complete(
44 | loop.create_datagram_endpoint(
45 | DNSProtocol, local_addr=('127.0.0.1', 4343)))
46 | print("DNS Server running")
47 |
48 | with suppress(KeyboardInterrupt):
49 | loop.run_forever()
50 | transport.close()
51 | loop.close()
52 |
53 | # nslookup -port=4343 buchuki.com localhost
54 |
--------------------------------------------------------------------------------
/Chapter13/13_09_async_multiprocessing.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import json
3 | from concurrent.futures import ProcessPoolExecutor
4 |
5 | def sort_in_process(data):
6 | nums = json.loads(data.decode())
7 | curr = 1
8 | while curr < len(nums):
9 | if nums[curr] >= nums[curr-1]:
10 | curr += 1
11 | else:
12 | nums[curr], nums[curr-1] = \
13 | nums[curr-1], nums[curr]
14 | if curr > 1:
15 | curr -= 1
16 |
17 | return json.dumps(nums).encode()
18 |
19 | @asyncio.coroutine
20 | def sort_request(reader, writer):
21 | print("Received connection")
22 | length = yield from reader.read(8)
23 | data = yield from reader.readexactly(int.from_bytes(length, 'big'))
24 | result = yield from asyncio.get_event_loop().run_in_executor(None, sort_in_process, data)
25 | print("Sorted list")
26 | writer.write(result)
27 | writer.close()
28 | print("Connection closed")
29 |
30 | loop = asyncio.get_event_loop()
31 | loop.set_default_executor(ProcessPoolExecutor())
32 | server = loop.run_until_complete(
33 | asyncio.start_server(sort_request, '127.0.0.1', 2015))
34 | print("Sort Service running")
35 |
36 | loop.run_forever()
37 | server.close()
38 | loop.run_until_complete(server.wait_closed())
39 | loop.close()
40 |
--------------------------------------------------------------------------------
/Chapter13/13_10_async_client.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import random
3 | import json
4 |
5 | @asyncio.coroutine
6 | def remote_sort():
7 | reader, writer = yield from asyncio.open_connection('127.0.0.1', 2015)
8 | print("Generating random list...")
9 | numbers = [random.randrange(10000) for r in range(10000)]
10 | data = json.dumps(numbers).encode()
11 | print("List Generated, Sending data")
12 | writer.write(len(data).to_bytes(8, 'big'))
13 | writer.write(data)
14 |
15 | print("Waiting for data...")
16 | data = yield from reader.readexactly(len(data))
17 | print("Received data")
18 | sorted_values = json.loads(data.decode())
19 | print(sorted_values)
20 | print('\n')
21 | writer.close()
22 |
23 | loop = asyncio.get_event_loop()
24 | loop.run_until_complete(remote_sort())
25 | loop.close()
26 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big2.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big3.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big4.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big5.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big6.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big7.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big8.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/bricks.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:e42a6abb2d9915c841300189cfafe30ddea27b2ee8df3134c42980abb475416f
3 | size 120122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/compress_bmp.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
3 |
4 | from PIL import Image
5 | from bitarray import bitarray
6 | from pathlib import Path
7 |
8 | def compress_row(row):
9 | compressed = bytearray()
10 | chunks = split_bits(row, 127)
11 | for chunk in chunks:
12 | compressed.extend(compress_chunk(chunk))
13 | return compressed
14 |
15 | def compress_chunk(chunk):
16 | compressed = bytearray()
17 | count = 1
18 | last = chunk[0]
19 | for bit in chunk[1:]:
20 | if bit != last:
21 | compressed.append(count | (128 * last))
22 | count = 0
23 | last = bit
24 | count += 1
25 | compressed.append(count | (128 * last))
26 | return compressed
27 |
28 |
29 | def split_bits(bits, width):
30 | for i in range(0, len(bits), width):
31 | yield bits[i:i+width]
32 |
33 | def compress_in_executor(executor, bits, width):
34 | row_compressors = []
35 | for row in split_bits(bits, width):
36 | compressor = executor.submit(compress_row, row)
37 | row_compressors.append(compressor)
38 |
39 | compressed = bytearray()
40 | for compressor in row_compressors:
41 | compressed.extend(compressor.result())
42 | return compressed
43 |
44 | def compress_image(in_filename, out_filename, executor=None):
45 | executor = executor if executor else ThreadPoolExecutor(4)
46 | with Image.open(in_filename) as image:
47 | bits = bitarray(image.convert('1').getdata())
48 | width, height = image.size
49 |
50 | compressed = compress_in_executor(executor, bits, width)
51 |
52 | with open(out_filename, 'wb') as file:
53 | file.write(width.to_bytes(2, 'little'))
54 | file.write(height.to_bytes(2, 'little'))
55 | file.write(compressed)
56 |
57 | def compress_dir(in_dir, out_dir):
58 | if not out_dir.exists():
59 | out_dir.mkdir()
60 |
61 | executor = ThreadPoolExecutor(4)
62 | for file in (f for f in in_dir.iterdir() if f.suffix == '.bmp'):
63 | out_file = (out_dir / file.name).with_suffix('.rle')
64 | executor.submit(compress_image, str(file), str(out_file))
65 |
66 | def single_image_main():
67 | in_filename, out_filename = sys.argv[1:3]
68 | executor = ThreadPoolExecutor(4)
69 | #executor = ProcessPoolExecutor()
70 | compress_image(in_filename, out_filename, executor)
71 |
72 | def dir_images_main():
73 | in_dir, out_dir = (Path(p) for p in sys.argv[1:3])
74 | compress_dir(in_dir, out_dir)
75 |
76 | if __name__ == '__main__':
77 | dir_images_main()
78 | #single_image_main()
79 |
--------------------------------------------------------------------------------
/Chapter13/case_study/decompress_to_bmp.py:
--------------------------------------------------------------------------------
1 | from PIL import Image
2 | import sys
3 |
4 | def decompress(width, height, bytes):
5 | image = Image.new('1', (width, height))
6 |
7 | col = 0
8 | row = 0
9 | for byte in bytes:
10 | color = (byte & 128) >> 7
11 | count = byte & ~128
12 | for i in range(count):
13 | image.putpixel((row, col), color)
14 | row += 1
15 | if not row % width:
16 | col += 1
17 | row = 0
18 | return image
19 |
20 |
21 | with open(sys.argv[1], 'rb') as file:
22 | width = int.from_bytes(file.read(2), 'little')
23 | height = int.from_bytes(file.read(2), 'little')
24 |
25 | image = decompress(width, height, file.read())
26 | image.save(sys.argv[2], 'bmp')
27 |
--------------------------------------------------------------------------------
/Chapter13/case_study/row.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:1e7b82cb4945f230f808b9b21ebc14dffb7e60be6fea68a877960d18ac80b5b7
3 | size 1322
4 |
--------------------------------------------------------------------------------
/Chapter3/3_01_inheriting_from_object.py:
--------------------------------------------------------------------------------
1 | class MySubClass(object):
2 | pass
3 |
--------------------------------------------------------------------------------
/Chapter3/3_02_simple_contact_class_to_inherit_from.py:
--------------------------------------------------------------------------------
1 | class Contact:
2 | all_contacts = []
3 |
4 | def __init__(self, name, email):
5 | self.name = name
6 | self.email = email
7 | Contact.all_contacts.append(self)
8 |
--------------------------------------------------------------------------------
/Chapter3/3_03_contact_inherit_supplier.py:
--------------------------------------------------------------------------------
1 | class Contact:
2 | all_contacts = []
3 |
4 | def __init__(self, name, email):
5 | self.name = name
6 | self.email = email
7 | Contact.all_contacts.append(self)
8 |
9 | class Supplier(Contact):
10 | def order(self, order):
11 | print("If this were a real system we would send "
12 | "{} order to {}".format(order, self.name))
13 |
--------------------------------------------------------------------------------
/Chapter3/3_04_contact_list_inheritance.py:
--------------------------------------------------------------------------------
1 | class ContactList(list):
2 | def search(self, name):
3 | '''Return all contacts that contain the search value
4 | in their name.'''
5 | matching_contacts = []
6 | for contact in self:
7 | if name in contact.name:
8 | matching_contacts.append(contact)
9 | return matching_contacts
10 |
11 | class Contact:
12 | all_contacts = ContactList()
13 |
14 | def __init__(self, name, email):
15 | self.name = name
16 | self.email = email
17 | self.all_contacts.append(self)
18 |
--------------------------------------------------------------------------------
/Chapter3/3_05_dictionary_long_name.py:
--------------------------------------------------------------------------------
1 | class LongNameDict(dict):
2 | def longest_key(self):
3 | longest = None
4 | for key in self:
5 | if not longest or len(key) > len(longest):
6 | longest = key
7 | return longest
8 |
--------------------------------------------------------------------------------
/Chapter3/3_06_friend_overrides_init.py:
--------------------------------------------------------------------------------
1 | class Contact:
2 | all_contacts = []
3 |
4 | def __init__(self, name, email):
5 | self.name = name
6 | self.email = email
7 | Contact.all_contacts.append(self)
8 |
9 | class Friend(Contact):
10 | def __init__(self, name, email, phone):
11 | self.name = name
12 | self.email = email
13 | self.phone = phone
14 |
--------------------------------------------------------------------------------
/Chapter3/3_07_friend_overrides_init_super.py:
--------------------------------------------------------------------------------
1 | class Contact:
2 | all_contacts = []
3 |
4 | def __init__(self, name, email):
5 | self.name = name
6 | self.email = email
7 | Contact.all_contacts.append(self)
8 |
9 | class Friend(Contact):
10 | def __init__(self, name, email, phone):
11 | super().__init__(name, email)
12 | self.phone = phone
13 |
--------------------------------------------------------------------------------
/Chapter3/3_08_send_mail.py:
--------------------------------------------------------------------------------
1 | class MailSender:
2 | def send_mail(self, message):
3 | print("Sending mail to " + self.email)
4 | # Add e-mail logic here
5 |
6 | class EmailableContact(Contact, MailSender):
7 | pass
8 |
--------------------------------------------------------------------------------
/Chapter3/3_09_send_mail_multi.py:
--------------------------------------------------------------------------------
1 | class MailSender:
2 | def send_mail(self, message):
3 | print("Sending mail to " + self.email)
4 | # Add e-mail logic here
5 |
6 | class EmailableContact(Friend, MailSender):
7 | pass
8 |
--------------------------------------------------------------------------------
/Chapter3/3_10_friend_address_holder.py:
--------------------------------------------------------------------------------
1 | class AddressHolder:
2 | def __init__(self, street, city, state, code):
3 | self.street = street
4 | self.city = city
5 | self.state = state
6 | self.code = code
7 |
--------------------------------------------------------------------------------
/Chapter3/3_11_friend_multi.py:
--------------------------------------------------------------------------------
1 | class Friend(Contact, AddressHolder):
2 | def __init__(self, name, email, phone,
3 | street, city, state, code):
4 | Contact.__init__(self, name, email)
5 | AddressHolder.__init__(
6 | self, street, city, state, code)
7 | self.phone = phone
8 |
--------------------------------------------------------------------------------
/Chapter3/3_12_contrived_diamond.py:
--------------------------------------------------------------------------------
1 | class BaseClass:
2 | num_base_calls = 0
3 | def call_me(self):
4 | print("Calling method on Base Class")
5 | self.num_base_calls += 1
6 |
7 | class LeftSubclass(BaseClass):
8 | num_left_calls = 0
9 | def call_me(self):
10 | BaseClass.call_me(self)
11 | print("Calling method on Left Subclass")
12 | self.num_left_calls += 1
13 |
14 | class RightSubclass(BaseClass):
15 | num_right_calls = 0
16 | def call_me(self):
17 | BaseClass.call_me(self)
18 | print("Calling method on Right Subclass")
19 | self.num_right_calls += 1
20 |
21 | class Subclass(LeftSubclass, RightSubclass):
22 | num_sub_calls = 0
23 | def call_me(self):
24 | LeftSubclass.call_me(self)
25 | RightSubclass.call_me(self)
26 | print("Calling method on Subclass")
27 | self.num_sub_calls += 1
28 |
--------------------------------------------------------------------------------
/Chapter3/3_14_contrived_diamond_super.py:
--------------------------------------------------------------------------------
1 | class BaseClass:
2 | num_base_calls = 0
3 | def call_me(self):
4 | print("Calling method on Base Class")
5 | self.num_base_calls += 1
6 |
7 | class LeftSubclass(BaseClass):
8 | num_left_calls = 0
9 | def call_me(self):
10 | super().call_me()
11 | print("Calling method on Left Subclass")
12 | self.num_left_calls += 1
13 |
14 | class RightSubclass(BaseClass):
15 | num_right_calls = 0
16 | def call_me(self):
17 | super().call_me()
18 | print("Calling method on Right Subclass")
19 | self.num_right_calls += 1
20 |
21 | class Subclass(LeftSubclass, RightSubclass):
22 | num_sub_calls = 0
23 | def call_me(self):
24 | super().call_me()
25 | print("Calling method on Subclass")
26 | self.num_sub_calls += 1
27 |
--------------------------------------------------------------------------------
/Chapter3/3_15_friend_multi_super.py:
--------------------------------------------------------------------------------
1 | class Contact:
2 | all_contacts = []
3 |
4 | def __init__(self, name='', email='', **kwargs):
5 | super().__init__(**kwargs)
6 | self.name = name
7 | self.email = email
8 | self.all_contacts.append(self)
9 |
10 | class AddressHolder:
11 | def __init__(self, street='', city='', state='', code='', **kwargs):
12 | super().__init__(**kwargs)
13 | self.street = street
14 | self.city = city
15 | self.state = state
16 | self.code = code
17 |
18 | class Friend(Contact, AddressHolder):
19 | def __init__(self, phone='', **kwargs):
20 | print(kwargs)
21 | super().__init__(**kwargs)
22 | self.phone = phone
23 |
--------------------------------------------------------------------------------
/Chapter3/3_16_polymorphic_audio.py:
--------------------------------------------------------------------------------
1 | class AudioFile:
2 | def __init__(self, filename):
3 | if not filename.endswith(self.ext):
4 | raise Exception("Invalid file format")
5 |
6 | self.filename = filename
7 |
8 | class MP3File(AudioFile):
9 | ext = "mp3"
10 | def play(self):
11 | print("playing {} as mp3".format(self.filename))
12 |
13 | class WavFile(AudioFile):
14 | ext = "wav"
15 | def play(self):
16 | print("playing {} as wav".format(self.filename))
17 |
18 | class OggFile(AudioFile):
19 | ext = "ogg"
20 | def play(self):
21 | print("playing {} as ogg".format(self.filename))
22 |
--------------------------------------------------------------------------------
/Chapter3/3_17.1_abc_container.py:
--------------------------------------------------------------------------------
1 |
2 | class OddContainer:
3 | def __contains__(self, x):
4 | if not isinstance(x, int) or not x % 2:
5 | return False
6 | return True
7 |
8 |
--------------------------------------------------------------------------------
/Chapter3/3_17.2_abc_media.py:
--------------------------------------------------------------------------------
1 | import abc
2 |
3 | class MediaLoader(metaclass=abc.ABCMeta):
4 | @abc.abstractmethod
5 | def play(self):
6 | pass
7 |
8 | @abc.abstractproperty
9 | def ext(self):
10 | pass
11 |
12 | @classmethod
13 | def __subclasshook__(cls, C):
14 | if cls is MediaLoader:
15 | attrs = set(dir(C))
16 | if set(cls.__abstractmethods__) <= attrs:
17 | return True
18 |
19 | return NotImplemented
20 |
--------------------------------------------------------------------------------
/Chapter3/3_17_ducktype_flac.py:
--------------------------------------------------------------------------------
1 | class FlacFile:
2 | def __init__(self, filename):
3 | if not filename.endswith(".flac"):
4 | raise Exception("Invalid file format")
5 |
6 | self.filename = filename
7 |
8 | def play(self):
9 | print("playing {} as flac movie".format(self.filename))
10 |
--------------------------------------------------------------------------------
/Chapter3/3_18_property.py:
--------------------------------------------------------------------------------
1 | class Property:
2 | def __init__(self, square_feet='', beds='',
3 | baths='', **kwargs):
4 | super().__init__(**kwargs)
5 | self.square_feet = square_feet
6 | self.num_bedrooms = beds
7 | self.num_baths = baths
8 |
9 | def display(self):
10 | print("PROPERTY DETAILS")
11 | print("================")
12 | print("square footage: {}".format(self.square_feet))
13 | print("bedrooms: {}".format(self.num_bedrooms))
14 | print("bathrooms: {}".format(self.num_baths))
15 | print()
16 |
17 | def prompt_init():
18 | return dict(square_feet=input("Enter the square feet: "),
19 | beds=input("Enter number of bedrooms: "),
20 | baths=input("Enter number of baths: "))
21 | prompt_init = staticmethod(prompt_init)
22 |
23 |
--------------------------------------------------------------------------------
/Chapter3/3_19_apartment_ugly_prompt.py:
--------------------------------------------------------------------------------
1 | class Apartment(Property):
2 | valid_laundries = ("coin", "ensuite", "none")
3 | valid_balconies = ("yes", "no", "solarium")
4 |
5 | def __init__(self, balcony='', laundry='', **kwargs):
6 | super().__init__(**kwargs)
7 | self.balcony = balcony
8 | self.laundry = laundry
9 |
10 | def display(self):
11 | super().display()
12 | print("APARTMENT DETAILS")
13 | print("laundry: {}".format(self.laundry))
14 | print("has balcony: {}".format(self.balcony))
15 |
16 | def prompt_init():
17 | parent_init = Property.prompt_init()
18 | laundry = ''
19 | while laundry.lower() not in \
20 | Apartment.valid_laundries:
21 | laundry = input("What laundry facilities does "
22 | "the property have? ({})".format(
23 | ", ".join(Apartment.valid_laundries))
24 | balcony = ''
25 | while balcony.lower() not in \
26 | Apartment.valid_balconies:
27 | balcony = input(
28 | "Does the property have a balcony? "
29 | "({})".format(
30 | ", ".join(Apartment.valid_balconies))
31 | parent_init.update({
32 | "laundry": laundry,
33 | "balcony": balcony
34 | })
35 | return parent_init
36 | prompt_init = staticmethod(prompt_init)
37 |
--------------------------------------------------------------------------------
/Chapter3/3_20_validation_function.py:
--------------------------------------------------------------------------------
1 | def get_valid_input(input_string, valid_options):
2 | input_string += " ({}) ".format(", ".join(valid_options))
3 | response = input(input_string)
4 | while response.lower() not in valid_options:
5 | response = input(input_string)
6 | return response
7 |
--------------------------------------------------------------------------------
/Chapter3/3_21_apartment_nice_prompt.py:
--------------------------------------------------------------------------------
1 | class Apartment(Property):
2 | valid_laundries = ("coin", "ensuite", "none")
3 | valid_balconies = ("yes", "no", "solarium")
4 |
5 | def __init__(self, balcony='', laundry='', **kwargs):
6 | super().__init__(**kwargs)
7 | self.balcony = balcony
8 | self.laundry = laundry
9 |
10 | def display(self):
11 | super().display()
12 | print("APARTMENT DETAILS")
13 | print("laundry: {}".format(self.laundry))
14 | print("has balcony: {}".format(self.balcony))
15 |
16 | def prompt_init():
17 | parent_init = Property.prompt_init()
18 | laundry = get_valid_input(
19 | "What laundry facilities does "
20 | "the property have? ",
21 | Apartment.valid_laundries)
22 | balcony = get_valid_input(
23 | "Does the property have a balcony? ",
24 | Apartment.valid_balconies)
25 | parent_init.update({
26 | "laundry": laundry,
27 | "balcony": balcony
28 | })
29 | return parent_init
30 | prompt_init = staticmethod(prompt_init)
31 |
--------------------------------------------------------------------------------
/Chapter3/3_22_house.py:
--------------------------------------------------------------------------------
1 | class House(Property):
2 | valid_garage = ("attached", "detached", "none")
3 | valid_fenced = ("yes", "no")
4 |
5 | def __init__(self, num_stories='',
6 | garage='', fenced='', **kwargs):
7 | super().__init__(**kwargs)
8 | self.garage = garage
9 | self.fenced = fenced
10 | self.num_stories = num_stories
11 |
12 | def display(self):
13 | super().display()
14 | print("HOUSE DETAILS")
15 | print("# of stories: {}".format(self.num_stories))
16 | print("garage: {}".format(self.garage))
17 | print("fenced yard: {}".format(self.fenced))
18 |
19 | def prompt_init():
20 | parent_init = Property.prompt_init()
21 | fenced = get_valid_input("Is the yard fenced? ",
22 | House.valid_fenced)
23 | garage = get_valid_input("Is there a garage? ",
24 | House.valid_garage)
25 | num_stories = input("How many stories? ")
26 |
27 | parent_init.update({
28 | "fenced": fenced,
29 | "garage": garage,
30 | "num_stories": num_stories
31 | })
32 | return parent_init
33 | prompt_init = staticmethod(prompt_init)
34 |
--------------------------------------------------------------------------------
/Chapter3/3_23_purchase_and_rental.py:
--------------------------------------------------------------------------------
1 | class Purchase:
2 | def __init__(self, price='', taxes='', **kwargs):
3 | super().__init__(**kwargs)
4 | self.price = price
5 | self.taxes = taxes
6 |
7 | def display(self):
8 | super().display()
9 | print("PURCHASE DETAILS")
10 | print("selling price: {}".format(self.price))
11 | print("estimated taxes: {}".format(self.taxes))
12 |
13 | def prompt_init():
14 | return dict(
15 | price=input("What is the selling price? "),
16 | taxes=input("What are the estimated taxes? "))
17 | prompt_init = staticmethod(prompt_init)
18 |
19 | class Rental:
20 | def __init__(self, furnished='', utilities='',
21 | rent='', **kwargs):
22 | super().__init__(**kwargs)
23 | self.furnished = furnished
24 | self.rent = rent
25 | self.utilities = utilities
26 |
27 | def display(self):
28 | super().display()
29 | print("RENTAL DETAILS")
30 | print("rent: {}".format(self.rent))
31 | print("estimated utilities: {}".format(
32 | self.utilities))
33 | print("furnished: {}".format(self.furnished))
34 |
35 | def prompt_init():
36 | return dict(
37 | rent=input("What is the monthly rent? "),
38 | utilities=input(
39 | "What are the estimated utilities? "),
40 | furnished = get_valid_input(
41 | "Is the property furnished? ",
42 | ("yes", "no")))
43 | prompt_init = staticmethod(prompt_init)
44 |
--------------------------------------------------------------------------------
/Chapter3/3_24_house_rental.py:
--------------------------------------------------------------------------------
1 | class HouseRental(Rental, House):
2 | def prompt_init():
3 | init = House.prompt_init()
4 | init.update(Rental.prompt_init())
5 | return init
6 | prompt_init = staticmethod(prompt_init)
7 |
8 |
--------------------------------------------------------------------------------
/Chapter3/3_25_remaining_subclasses.py:
--------------------------------------------------------------------------------
1 | class ApartmentRental(Rental, Apartment):
2 | def prompt_init():
3 | init = Apartment.prompt_init()
4 | init.update(Rental.prompt_init())
5 | return init
6 | prompt_init = staticmethod(prompt_init)
7 |
8 | class ApartmentPurchase(Purchase, Apartment):
9 | def prompt_init():
10 | init = Apartment.prompt_init()
11 | init.update(Purchase.prompt_init())
12 | return init
13 | prompt_init = staticmethod(prompt_init)
14 |
15 | class HousePurchase(Purchase, House):
16 | def prompt_init():
17 | init = House.prompt_init()
18 | init.update(Purchase.prompt_init())
19 | return init
20 | prompt_init = staticmethod(prompt_init)
21 |
--------------------------------------------------------------------------------
/Chapter3/3_26_rudimentary_agent.py:
--------------------------------------------------------------------------------
1 | class Agent:
2 | def __init__(self):
3 | self.property_list = []
4 |
5 | def display_properties():
6 | for property in self.property_list:
7 | property.display()
8 |
--------------------------------------------------------------------------------
/Chapter3/3_27_type_map.py:
--------------------------------------------------------------------------------
1 | class Agent:
2 | type_map = {
3 | ("house", "rental"): HouseRental,
4 | ("house", "purchase"): HousePurchase,
5 | ("apartment", "rental"): ApartmentRental,
6 | ("apartment", "purchase"): ApartmentPurchase
7 | }
8 |
9 | def __init__(self):
10 | self.property_list = []
11 |
12 | def display_properties():
13 | for property in self.property_list:
14 | property.display()
15 |
--------------------------------------------------------------------------------
/Chapter3/3_28_add_property.py:
--------------------------------------------------------------------------------
1 | class Agent:
2 | type_map = {
3 | ("house", "rental"): HouseRental,
4 | ("house", "purchase"): HousePurchase,
5 | ("apartment", "rental"): ApartmentRental,
6 | ("apartment", "purchase"): ApartmentPurchase
7 | }
8 |
9 | def __init__(self):
10 | self.property_list = []
11 |
12 | def display_properties():
13 | for property in self.property_list:
14 | property.display()
15 |
16 | def add_property(self):
17 | property_type = get_valid_input(
18 | "What type of property? ",
19 | ("house", "apartment")).lower()
20 | payment_type = get_valid_input(
21 | "What payment type? ",
22 | ("purchase", "rental")).lower()
23 |
24 | PropertyClass = self.type_map[(property_type, payment_type)]
25 | init_args = PropertyClass.prompt_init()
26 | self.property_list.append(PropertyClass(**init_args))
27 |
--------------------------------------------------------------------------------
/Chapter4/04_03_method_calls_excepting.py:
--------------------------------------------------------------------------------
1 | def no_return():
2 | print("I am about to raise an exception")
3 | raise Exception("This is always raised")
4 | print("This line will never execute")
5 | return "I won't be returned"
6 |
7 | def call_exceptor():
8 | print("call_exceptor starts here...")
9 | no_return()
10 | print("an exception was raised...")
11 | print("...so these lines don't run")
12 |
--------------------------------------------------------------------------------
/Chapter4/4_01_even_integers.py:
--------------------------------------------------------------------------------
1 | class EvenOnly(list):
2 | def append(self, integer):
3 | if not isinstance(integer, int):
4 | raise TypeError("Only integers can be added")
5 | if integer % 2:
6 | raise ValueError("Only even numbers can be added")
7 | super().append(integer)
8 |
--------------------------------------------------------------------------------
/Chapter4/4_02_exception_quits.py:
--------------------------------------------------------------------------------
1 | def no_return():
2 | print("I am about to raise an exception")
3 | raise Exception("This is always raised")
4 | print("This line will never execute")
5 | return "I won't be returned"
6 |
7 |
--------------------------------------------------------------------------------
/Chapter4/4_04_try_except.py:
--------------------------------------------------------------------------------
1 | def no_return():
2 | print("I am about to raise an exception")
3 | raise Exception("This is always raised")
4 | print("This line will never execute")
5 | return "I won't be returned"
6 |
7 | try:
8 | no_return()
9 | except:
10 | print("I caught an exception")
11 | print("executed after the exception")
12 |
--------------------------------------------------------------------------------
/Chapter4/4_05_catch_specific_exception.py:
--------------------------------------------------------------------------------
1 | def funny_division(anumber):
2 | try:
3 | return 100 / anumber
4 | except ZeroDivisionError:
5 | return "Silly wabbit, you can't divide by zero!"
6 |
7 | print(funny_division(0))
8 | print(funny_division(50.0))
9 | print(funny_division("hello"))
10 |
--------------------------------------------------------------------------------
/Chapter4/4_06_catch_multiple_exceptions.py:
--------------------------------------------------------------------------------
1 | def funny_division2(anumber):
2 | try:
3 | if anumber == 13:
4 | raise ValueError("13 is an unlucky number")
5 | return 100 / anumber
6 | except (ZeroDivisionError, TypeError):
7 | return "Enter a number other than zero"
8 |
9 | for val in (0, "hello", 50.0, 13):
10 | print("Testing {}:".format(val), end=" ")
11 | print(funny_division2(val))
12 |
--------------------------------------------------------------------------------
/Chapter4/4_07_catch_multiple_different.py:
--------------------------------------------------------------------------------
1 | def funny_division3(anumber):
2 | try:
3 | if anumber == 13:
4 | raise ValueError("13 is an unlucky number")
5 | return 100 / anumber
6 | except ZeroDivisionError:
7 | return "Enter a number other than zero"
8 | except TypeError:
9 | return "Enter a numerical value"
10 | except ValueError:
11 | print("No, No, not 13!")
12 | raise
13 |
14 | for val in (0, "hello", 50.0, 13):
15 | print("Testing %s:" % val, end=" ")
16 | print(funny_division3(val))
17 |
--------------------------------------------------------------------------------
/Chapter4/4_08_catch_as_keyword.py:
--------------------------------------------------------------------------------
1 | try:
2 | raise ValueError("This is an argument")
3 | except ValueError as e:
4 | print("The exception arguments were", e.args)
5 |
--------------------------------------------------------------------------------
/Chapter4/4_09_finally_and_else.py:
--------------------------------------------------------------------------------
1 | import random
2 | some_exceptions = [ValueError, TypeError, IndexError, None]
3 |
4 | try:
5 | choice = random.choice(some_exceptions)
6 | print("raising {}".format(choice))
7 | if choice:
8 | raise choice("An error")
9 | except ValueError:
10 | print("Caught a ValueError")
11 | except TypeError:
12 | print("Caught a TypeError")
13 | except Exception as e:
14 | print("Caught some other error: %s" % e.__class__.__name__)
15 | else:
16 | print("This code called if there is no exception")
17 | finally:
18 | print("This cleanup code is always called")
19 |
--------------------------------------------------------------------------------
/Chapter4/4_10_defining_an_exception.py:
--------------------------------------------------------------------------------
1 | class InvalidWithdrawal(Exception):
2 | pass
3 |
4 | raise InvalidWithdrawal("You don't have $50 in your account")
5 |
--------------------------------------------------------------------------------
/Chapter4/4_11_exception_with_custom_args.py:
--------------------------------------------------------------------------------
1 | class InvalidWithdrawal(Exception):
2 | def __init__(self, balance, amount):
3 | super().__init__("account doesn't have ${}".format(
4 | amount))
5 | self.amount = amount
6 | self.balance = balance
7 |
8 | def overage(self):
9 | return self.amount - self.balance
10 |
11 | raise InvalidWithdrawal(25, 50)
12 |
--------------------------------------------------------------------------------
/Chapter4/4_12_handle_custom_exception.py:
--------------------------------------------------------------------------------
1 | class InvalidWithdrawal(Exception):
2 | def __init__(self, balance, amount):
3 | super().__init__("account doesn't have ${}".format(
4 | amount))
5 | self.amount = amount
6 | self.balance = balance
7 |
8 | def overage(self):
9 | return self.amount - self.balance
10 |
11 | try:
12 | raise InvalidWithdrawal(25, 50)
13 | except InvalidWithdrawal as e:
14 | print("I'm sorry, but your withdrawal is "
15 | "more than your balance by "
16 | "${}".format(e.overage()))
17 |
--------------------------------------------------------------------------------
/Chapter4/4_13_branching_vs_exceptions.py:
--------------------------------------------------------------------------------
1 | def divide_with_exception(number, divisor):
2 | try:
3 | print("{} / {} = {}".format(
4 | number, divisor, number / divisor * 1.0))
5 | except ZeroDivisionError:
6 | print("You can't divide by zero")
7 |
8 | def divide_with_if(number, divisor):
9 | if divisor == 0:
10 | print("You can't divide by zero")
11 | else:
12 | print("{} / {} = {}".format(
13 | number, divisor, number / divisor * 1.0))
14 |
15 | divide_with_exception(10, 5)
16 | divide_with_if(10, 5)
17 | divide_with_if(10, 0)
18 | divide_with_exception(10, 0)
19 |
--------------------------------------------------------------------------------
/Chapter4/4_14_inventory_mock_object.py:
--------------------------------------------------------------------------------
1 | class Inventory:
2 | def lock(self, item_type):
3 | '''Select the type of item that is going to
4 | be manipulated. This method will lock the
5 | item so nobody else can manipulate the
6 | inventory until it's returned. This prevents
7 | selling the same item to two different
8 | customers.'''
9 | pass
10 |
11 | def unlock(self, item_type):
12 | '''Release the given type so that other
13 | customers can access it.'''
14 | pass
15 |
16 | def purchase(self, item_type):
17 | '''If the item is not locked, raise an
18 | exception. If the itemtype does not exist,
19 | raise an exception. If the item is currently
20 | out of stock, raise an exception. If the item
21 | is available, subtract one item and return
22 | the number of items left.'''
23 | pass
24 |
25 |
--------------------------------------------------------------------------------
/Chapter4/4_15_inventory_handling.py:
--------------------------------------------------------------------------------
1 | class Inventory:
2 | def lock(self, item_type):
3 | pass
4 |
5 | def unlock(self, item_type):
6 | pass
7 |
8 | def purchase(self, item_type):
9 | pass
10 |
11 | item_type = 'widget'
12 | inv = Inventory()
13 | inv.lock(item_type)
14 | try:
15 | num_left = inv.purchase(item_type)
16 | except InvalidItemType:
17 | print("Sorry, we don't sell {}".format(item_type))
18 | except OutOfStock:
19 | print("Sorry, that item is out of stock.")
20 | else:
21 | print("Purchase complete. There are "
22 | "{} {}s left".format(num_left, item_type))
23 | finally:
24 | inv.unlock()
25 |
--------------------------------------------------------------------------------
/Chapter4/4_16_auth_user.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 |
3 | class User:
4 | def __init__(self, username, password):
5 | '''Create a new user object. The password
6 | will be encrypted before storing.'''
7 | self.username = username
8 | self.password = self._encrypt_pw(password)
9 | self.is_logged_in = False
10 |
11 | def _encrypt_pw(self, password):
12 | '''Encrypt the password with the username and return
13 | the sha digest.'''
14 | hash_string = (self.username + password)
15 | hash_string = hash_string.encode("utf8")
16 | return hashlib.sha256(hash_string).hexdigest()
17 |
18 | def check_password(self, password):
19 | '''Return True if the password is valid for this
20 | user, false otherwise.'''
21 | encrypted = self._encrypt_pw(password)
22 | return encrypted == self.password
23 |
24 |
25 | class AuthException(Exception):
26 | def __init__(self, username, user=None):
27 | super().__init__(username)
28 | self.username = username
29 | self.user = user
30 |
31 | class UsernameAlreadyExists(AuthException):
32 | pass
33 |
34 | class PasswordTooShort(AuthException):
35 | pass
36 |
--------------------------------------------------------------------------------
/Chapter4/4_17_authenticator.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 |
3 | class User:
4 | def __init__(self, username, password):
5 | '''Create a new user object. The password
6 | will be encrypted before storing.'''
7 | self.username = username
8 | self.password = self._encrypt_pw(password)
9 | self.is_logged_in = False
10 |
11 | def _encrypt_pw(self, password):
12 | '''Encrypt the password with the username and return
13 | the sha digest.'''
14 | hash_string = (self.username + password)
15 | hash_string = hash_string.encode("utf8")
16 | return hashlib.sha256(hash_string).hexdigest()
17 |
18 | def check_password(self, password):
19 | '''Return True if the password is valid for this
20 | user, false otherwise.'''
21 | encrypted = self._encrypt_pw(password)
22 | return encrypted == self.password
23 |
24 |
25 | class AuthException(Exception):
26 | def __init__(self, username, user=None):
27 | super().__init__(username)
28 | self.username = username
29 | self.user = user
30 |
31 | class UsernameAlreadyExists(AuthException):
32 | pass
33 |
34 | class PasswordTooShort(AuthException):
35 | pass
36 |
37 | class Authenticator:
38 | def __init__(self):
39 | '''Construct an authenticator to manage
40 | users logging in and out.'''
41 | self.users = {}
42 |
43 | def add_user(self, username, password):
44 | if username in self.users:
45 | raise UsernameAlreadyExists(username)
46 | if len(password) < 6:
47 | raise PasswordTooShort(username)
48 | self.users[username] = User(username, password)
49 |
--------------------------------------------------------------------------------
/Chapter4/4_18_login.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 |
3 | class User:
4 | def __init__(self, username, password):
5 | '''Create a new user object. The password
6 | will be encrypted before storing.'''
7 | self.username = username
8 | self.password = self._encrypt_pw(password)
9 | self.is_logged_in = False
10 |
11 | def _encrypt_pw(self, password):
12 | '''Encrypt the password with the username and return
13 | the sha digest.'''
14 | hash_string = (self.username + password)
15 | hash_string = hash_string.encode("utf8")
16 | return hashlib.sha256(hash_string).hexdigest()
17 |
18 | def check_password(self, password):
19 | '''Return True if the password is valid for this
20 | user, false otherwise.'''
21 | encrypted = self._encrypt_pw(password)
22 | return encrypted == self.password
23 |
24 |
25 | class AuthException(Exception):
26 | def __init__(self, username, user=None):
27 | super().__init__(username)
28 | self.username = username
29 | self.user = user
30 |
31 | class UsernameAlreadyExists(AuthException):
32 | pass
33 |
34 | class PasswordTooShort(AuthException):
35 | pass
36 |
37 | class InvalidUsername(AuthException):
38 | pass
39 |
40 | class InvalidPassword(AuthException):
41 | pass
42 |
43 | class Authenticator:
44 | def __init__(self):
45 | '''Construct an authenticator to manage
46 | users logging in and out.'''
47 | self.users = {}
48 |
49 | def add_user(self, username, password):
50 | if username in self.users:
51 | raise UsernameAlreadyExists(username)
52 | if len(password) < 6:
53 | raise PasswordTooShort(username)
54 | self.users[username] = User(username, password)
55 |
56 | def login(self, username, password):
57 | try:
58 | user = self.users[username]
59 | except KeyError:
60 | raise InvalidUsername(username)
61 |
62 | if not user.check_password(password):
63 | raise InvalidPassword(username, user)
64 |
65 | user.is_logged_in = True
66 | return True
67 |
68 | def is_logged_in(self, username):
69 | if username in self.users:
70 | return self.users[username].is_logged_in
71 | return False
72 |
73 | authenticator = Authenticator()
74 |
--------------------------------------------------------------------------------
/Chapter4/4_20_test_auth.py:
--------------------------------------------------------------------------------
1 | import auth
2 |
3 | # Set up a test user and permission
4 | auth.authenticator.add_user("joe", "joepassword")
5 | auth.authorizor.add_permission("test program")
6 | auth.authorizor.add_permission("change program")
7 | auth.authorizor.permit_user("test program", "joe")
8 |
9 | class Editor:
10 | def __init__(self):
11 | self.username = None
12 | self.menu_map = {
13 | "login": self.login,
14 | "test": self.test,
15 | "change": self.change,
16 | "quit": self.quit
17 | }
18 |
19 | def login(self):
20 | logged_in = False
21 | while not logged_in:
22 | username = input("username: ")
23 | password = input("password: ")
24 | try:
25 | logged_in = auth.authenticator.login(
26 | username, password)
27 | except auth.InvalidUsername:
28 | print("Sorry, that username does not exist")
29 | except auth.InvalidPassword:
30 | print("Sorry, incorrect password")
31 | else:
32 | self.username = username
33 |
34 | def is_permitted(self, permission):
35 | try:
36 | auth.authorizor.check_permission(
37 | permission, self.username)
38 | except auth.NotLoggedInError as e:
39 | print("{} is not logged in".format(e.username))
40 | return False
41 | except auth.NotPermittedError as e:
42 | print("{} cannot {}".format(
43 | e.username, permission))
44 | return False
45 | else:
46 | return True
47 |
48 | def test(self):
49 | if self.is_permitted("test program"):
50 | print("Testing program now...")
51 |
52 | def change(self):
53 | if self.is_permitted("change program"):
54 | print("Changing program now...")
55 |
56 | def quit(self):
57 | raise SystemExit()
58 |
59 | def menu(self):
60 | try:
61 | answer = ""
62 | while True:
63 | print("""
64 | Please enter a command:
65 | \tlogin\tLogin
66 | \ttest\tTest the program
67 | \tchange\tChange the program
68 | \tquit\tQuit
69 | """)
70 | answer = input("enter a command: ").lower()
71 | try:
72 | func = self.menu_map[answer]
73 | except KeyError:
74 | print("{} is not a valid option".format(
75 | answer))
76 | else:
77 | func()
78 | finally:
79 | print("Thank you for testing the auth module")
80 |
81 |
82 | Editor().menu()
83 |
--------------------------------------------------------------------------------
/Chapter4/__pycache__/auth.cpython-34.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter4/__pycache__/auth.cpython-34.pyc
--------------------------------------------------------------------------------
/Chapter4/auth.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter4/auth.pyc
--------------------------------------------------------------------------------
/Chapter5/5_01_distances_no_objects.py:
--------------------------------------------------------------------------------
1 | import math
2 |
3 | def distance(p1, p2):
4 | return math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2)
5 |
6 | def perimeter(polygon):
7 | perimeter = 0
8 | points = polygon + [polygon[0]]
9 | for i in range(len(polygon)):
10 | perimeter += distance(points[i], points[i+1])
11 | return perimeter
12 |
--------------------------------------------------------------------------------
/Chapter5/5_02_distances_by_object.py:
--------------------------------------------------------------------------------
1 | import math
2 |
3 | class Point:
4 | def __init__(self, x, y):
5 | self.x = x
6 | self.y = y
7 |
8 | def distance(self, p2):
9 | return math.sqrt((self.x-p2.x)**2 + (self.y-p2.y)**2)
10 |
11 | class Polygon:
12 | def __init__(self):
13 | self.vertices = []
14 |
15 | def add_point(self, point):
16 | self.vertices.append((point))
17 |
18 | def perimeter(self):
19 | perimeter = 0
20 | points = self.vertices + [self.vertices[0]]
21 | for i in range(len(self.vertices)):
22 | perimeter += points[i].distance(points[i+1])
23 | return perimeter
24 |
--------------------------------------------------------------------------------
/Chapter5/5_03_object_polygon_init.py:
--------------------------------------------------------------------------------
1 | import math
2 |
3 | class Point:
4 | def __init__(self, x, y):
5 | self.x = x
6 | self.y = y
7 |
8 | def distance(self, p2):
9 | return math.sqrt((self.x-p2.x)**2 + (self.y-p2.y)**2)
10 |
11 | class Polygon:
12 | def __init__(self, points = []):
13 | self.vertices = []
14 | for point in points:
15 | if isinstance(point, tuple):
16 | point = Point(*point)
17 | self.vertices.append(point)
18 |
19 | def add_point(self, point):
20 | self.vertices.append((point))
21 |
22 | def perimeter(self):
23 | perimeter = 0
24 | points = self.vertices + [self.vertices[0]]
25 | for i in range(len(self.vertices)):
26 | perimeter += points[i].distance(points[i+1])
27 | return perimeter
28 |
--------------------------------------------------------------------------------
/Chapter5/5_04_pytho_ugly_as_java.py:
--------------------------------------------------------------------------------
1 | class Color:
2 | def __init__(self, rgb_value, name):
3 | self._rgb_value = rgb_value
4 | self._name = name
5 |
6 | def set_name(self, name):
7 | self._name = name
8 |
9 | def get_name(self):
10 | return self._name
11 |
--------------------------------------------------------------------------------
/Chapter5/5_05_python_pretty_as_python.py:
--------------------------------------------------------------------------------
1 | class Color:
2 | def __init__(self, rgb_value, name):
3 | self.rgb_value = rgb_value
4 | self.name = name
5 |
6 | c = Color("#ff0000", "bright red")
7 | print(c.name)
8 | c.name = "red"
9 |
--------------------------------------------------------------------------------
/Chapter5/5_06_setting_name_in_method.py:
--------------------------------------------------------------------------------
1 | class Color:
2 | def __init__(self, rgb_value, name):
3 | self._rgb_value = rgb_value
4 | self._name = name
5 |
6 | def set_name(self, name):
7 | if not name:
8 | raise Exception("Invalid Name")
9 | self._name = name
10 |
11 | def get_name(self):
12 | return self._name
13 |
--------------------------------------------------------------------------------
/Chapter5/5_07_setting_name_property.py:
--------------------------------------------------------------------------------
1 | class Color:
2 | def __init__(self, rgb_value, name):
3 | self.rgb_value = rgb_value
4 | self._name = name
5 |
6 | def _set_name(self, name):
7 | if not name:
8 | raise Exception("Invalid Name")
9 | self._name = name
10 |
11 | def _get_name(self):
12 | return self._name
13 |
14 | name = property(_get_name, _set_name)
15 |
--------------------------------------------------------------------------------
/Chapter5/5_08_property_arguments.py:
--------------------------------------------------------------------------------
1 | class Silly:
2 | def _get_silly(self):
3 | print("You are getting silly")
4 | return self._silly
5 | def _set_silly(self, value):
6 | print("You are making silly {}".format(value))
7 | self._silly = value
8 | def _del_silly(self):
9 | print("Whoah, you killed silly!")
10 | del self._silly
11 |
12 | silly = property(_get_silly, _set_silly, _del_silly,
13 | "This is a silly property")
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Chapter5/5_09_property_decorator_get.py:
--------------------------------------------------------------------------------
1 | class Foo:
2 | @property
3 | def foo(self):
4 | return "bar"
5 |
--------------------------------------------------------------------------------
/Chapter5/5_11_property_decorator_arguments.py:
--------------------------------------------------------------------------------
1 | class Silly:
2 | @property
3 | def silly(self):
4 | "This is a silly property"
5 | print("You are getting silly")
6 | return self._silly
7 |
8 | @silly.setter
9 | def silly(self, value):
10 | print("You are making silly {}".format(value))
11 | self._silly = value
12 |
13 | @silly.deleter
14 | def silly(self):
15 | print("Whoah, you killed silly!")
16 | del self._silly
17 |
--------------------------------------------------------------------------------
/Chapter5/5_12_property_decorator_get_set.py:
--------------------------------------------------------------------------------
1 | class Foo:
2 | @property
3 | def foo(self):
4 | return self._foo
5 |
6 | @foo.setter
7 | def foo(self, value):
8 | self._foo = value
9 |
10 |
--------------------------------------------------------------------------------
/Chapter5/5_13_read_only_setattr.py:
--------------------------------------------------------------------------------
1 | class ReadOnlyX:
2 | def __setattr__(self, attr, value):
3 | if attr == "x":
4 | raise AttributeError("X is immutable")
5 | super().__setattr__(attr, value)
6 |
--------------------------------------------------------------------------------
/Chapter5/5_14_read_only_getattribute.py:
--------------------------------------------------------------------------------
1 | class ReadOnlyY:
2 | def __getattribute__(self, attr):
3 | if attr == "y":
4 | return "Just Try and Change Me!"
5 | return super().__getattribute__(attr)
6 |
--------------------------------------------------------------------------------
/Chapter5/5_15_cache_getter.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 |
3 | class WebPage:
4 | def __init__(self, url):
5 | self.url = url
6 | self._content = None
7 |
8 | @property
9 | def content(self):
10 | if not self._content:
11 | print("Retrieving New Page...")
12 | self._content = urlopen(self.url).read()
13 | return self._content
14 |
--------------------------------------------------------------------------------
/Chapter5/5_16_average_property.py:
--------------------------------------------------------------------------------
1 | class AverageList(list):
2 | @property
3 | def average(self):
4 | return sum(self) / len(self)
5 |
--------------------------------------------------------------------------------
/Chapter5/5_17_zipsearch.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import shutil
3 | import zipfile
4 | from pathlib import Path
5 |
6 | class ZipReplace:
7 | def __init__(self, filename, search_string, replace_string):
8 | self.filename = filename
9 | self.search_string = search_string
10 | self.replace_string = replace_string
11 | self.temp_directory = Path("unzipped-{}".format(
12 | filename))
13 |
14 | def zip_find_replace(self):
15 | self.unzip_files()
16 | self.find_replace()
17 | self.zip_files()
18 |
19 | def unzip_files(self):
20 | self.temp_directory.mkdir()
21 | with zipfile.ZipFile(self.filename) as zip:
22 | zip.extractall(str(self.temp_directory))
23 |
24 | def find_replace(self):
25 | for filename in self.temp_directory.iterdir():
26 | with filename.open() as file:
27 | contents = file.read()
28 | contents = contents.replace(
29 | self.search_string, self.replace_string)
30 | with filename.open("w") as file:
31 | file.write(contents)
32 |
33 | def zip_files(self):
34 | with zipfile.ZipFile(self.filename, 'w') as file:
35 | for filename in self.temp_directory.iterdir():
36 | file.write(str(filename), filename.name)
37 | shutil.rmtree(str(self.temp_directory))
38 |
39 | if __name__ == "__main__":
40 | ZipReplace(*sys.argv[1:4]).zip_find_replace()
41 |
--------------------------------------------------------------------------------
/Chapter5/5_18_zipprocessor.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 | import zipfile
4 | from pathlib import Path
5 |
6 |
7 | class ZipProcessor:
8 | def __init__(self, zipname):
9 | self.zipname = zipname
10 | self.temp_directory = Path("unzipped-{}".format(
11 | zipname[:-4]))
12 |
13 | def process_zip(self):
14 | self.unzip_files()
15 | self.process_files()
16 | self.zip_files()
17 |
18 | def unzip_files(self):
19 | self.temp_directory.mkdir()
20 | with zipfile.ZipFile(self.zipname) as zip:
21 | zip.extractall(str(self.temp_directory))
22 |
23 | def zip_files(self):
24 | with zipfile.ZipFile(self.zipname, 'w') as file:
25 | for filename in self.temp_directory.iterdir():
26 | file.write(str(filename), filename.name)
27 | shutil.rmtree(str(self.temp_directory))
28 |
--------------------------------------------------------------------------------
/Chapter5/5_19_zipreplace_inheritance.py:
--------------------------------------------------------------------------------
1 | from zip_processor import ZipProcessor
2 | import sys
3 | import os
4 |
5 | class ZipReplace(ZipProcessor):
6 | def __init__(self, filename, search_string,
7 | replace_string):
8 | super().__init__(filename)
9 | self.search_string = search_string
10 | self.replace_string = replace_string
11 |
12 | def process_files(self):
13 | '''perform a search and replace on all files in the
14 | temporary directory'''
15 | for filename in self.temp_directory.iterdir():
16 | with filename.open() as file:
17 | contents = file.read()
18 | contents = contents.replace(
19 | self.search_string, self.replace_string)
20 | with filename.open("w") as file:
21 | file.write(contents)
22 |
23 | if __name__ == "__main__":
24 | ZipReplace(*sys.argv[1:4]).process_zip()
25 |
--------------------------------------------------------------------------------
/Chapter5/5_20_scaleimage_inheritance.py:
--------------------------------------------------------------------------------
1 | from zip_processor import ZipProcessor
2 | import sys
3 | from PIL import Image
4 |
5 | class ScaleZip(ZipProcessor):
6 |
7 | def process_files(self):
8 | '''Scale each image in the directory to 640x480'''
9 | for filename in self.temp_directory.iterdir():
10 | im = Image.open(str(filename))
11 | scaled = im.resize((640, 480))
12 | scaled.save(str(filename))
13 |
14 | if __name__ == "__main__":
15 | ScaleZip(*sys.argv[1:4]).process_zip()
16 |
--------------------------------------------------------------------------------
/Chapter5/5_24_most_basic_document.py:
--------------------------------------------------------------------------------
1 | class Document:
2 | def __init__(self):
3 | self.characters = []
4 | self.cursor = 0
5 | self.filename = ''
6 |
7 | def insert(self, character):
8 | self.characters.insert(self.cursor, character)
9 | self.cursor += 1
10 |
11 | def delete(self):
12 | del self.characters[self.cursor]
13 |
14 | def save(self):
15 | with open(self.filename, 'w') as f:
16 | f.write(''.join(self.characters))
17 |
18 | def forward(self):
19 | self.cursor += 1
20 |
21 | def back(self):
22 | self.cursor -= 1
23 |
--------------------------------------------------------------------------------
/Chapter5/5_25_document_cursor.py:
--------------------------------------------------------------------------------
1 | class Cursor:
2 | def __init__(self, document):
3 | self.document = document
4 | self.position = 0
5 |
6 | def forward(self):
7 | self.position += 1
8 |
9 | def back(self):
10 | self.position -= 1
11 |
12 | def home(self):
13 | while self.document.characters[
14 | self.position-1] != '\n':
15 | self.position -= 1
16 | if self.position == 0:
17 | # Got to beginning of file before newline
18 | break
19 |
20 | def end(self):
21 | while self.position < len(self.document.characters
22 | ) and self.document.characters[self.position] != '\n':
23 | self.position += 1
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Chapter5/5_26_document_using_cursor.py:
--------------------------------------------------------------------------------
1 | class Cursor:
2 | def __init__(self, document):
3 | self.document = document
4 | self.position = 0
5 |
6 | def forward(self):
7 | self.position += 1
8 |
9 | def back(self):
10 | self.position -= 1
11 |
12 | def home(self):
13 | while self.document.characters[
14 | self.position-1] != '\n':
15 | self.position -= 1
16 | if self.position == 0:
17 | # Got to beginning of file before newline
18 | break
19 |
20 | def end(self):
21 | while self.position < len(self.document.characters
22 | ) and self.document.characters[self.position] != '\n':
23 | self.position += 1
24 |
25 |
26 | class Document:
27 | def __init__(self):
28 | self.characters = []
29 | self.cursor = Cursor(self)
30 | self.filename = ''
31 |
32 | def insert(self, character):
33 | self.characters.insert(self.cursor.position,
34 | character)
35 | self.cursor.forward()
36 |
37 | def delete(self):
38 | del self.characters[self.cursor.position]
39 |
40 | def save(self):
41 | f = open(self.filename, 'w')
42 | f.write(''.join(self.characters))
43 | f.close()
44 |
--------------------------------------------------------------------------------
/Chapter5/5_27_string_property.py:
--------------------------------------------------------------------------------
1 | @property
2 | def string(self):
3 | return "".join(self.characters)
4 |
--------------------------------------------------------------------------------
/Chapter5/5_28_Character_class.py:
--------------------------------------------------------------------------------
1 | class Character:
2 | def __init__(self, character,
3 | bold=False, italic=False, underline=False):
4 | assert len(character) == 1
5 | self.character = character
6 | self.bold = bold
7 | self.italic = italic
8 | self.underline = underline
9 |
10 | def __str__(self):
11 | bold = "*" if self.bold else ''
12 | italic = "/" if self.italic else ''
13 | underline = "_" if self.underline else ''
14 | return bold + italic + underline + self.character
15 |
--------------------------------------------------------------------------------
/Chapter5/5_29_document_with_character.py:
--------------------------------------------------------------------------------
1 | class Document:
2 | def __init__(self):
3 | self.characters = []
4 | self.cursor = Cursor(self)
5 | self.filename = ''
6 |
7 | def insert(self, character):
8 | if not hasattr(character, 'character'):
9 | character = Character(character)
10 | self.characters.insert(self.cursor.position,
11 | character)
12 | self.cursor.forward()
13 |
14 | def delete(self):
15 | del self.characters[self.cursor.position]
16 |
17 | def save(self):
18 | f = open(self.filename, 'w')
19 | f.write(''.join(self.characters))
20 | f.close()
21 |
22 | @property
23 | def string(self):
24 | return "".join((str(c) for c in self.characters))
25 |
26 |
27 | class Cursor:
28 | def __init__(self, document):
29 | self.document = document
30 | self.position = 0
31 |
32 | def forward(self):
33 | self.position += 1
34 |
35 | def back(self):
36 | self.position -= 1
37 |
38 | def home(self):
39 | while self.document.characters[
40 | self.position-1].character != '\n':
41 | self.position -= 1
42 | if self.position == 0:
43 | # Got to beginning of file before newline
44 | break
45 |
46 | def end(self):
47 | while self.position < len(self.document.characters) and \
48 | self.document.characters[
49 | self.position].character != '\n':
50 | self.position += 1
51 |
52 | class Character:
53 | def __init__(self, character,
54 | bold=False, italic=False, underline=False):
55 | assert len(character) == 1
56 | self.character = character
57 | self.bold = bold
58 | self.italic = italic
59 | self.underline = underline
60 |
61 | def __str__(self):
62 | bold = "*" if self.bold else ''
63 | italic = "/" if self.italic else ''
64 | underline = "_" if self.underline else ''
65 | return bold + italic + underline + self.character
66 |
--------------------------------------------------------------------------------
/Chapter5/Document.py:
--------------------------------------------------------------------------------
1 | class Document:
2 | def __init__(self):
3 | self.characters = []
4 | self.cursor = Cursor(self)
5 | self.filename = ''
6 |
7 | def insert(self, character):
8 | if not hasattr(character, 'character'):
9 | character = Character(character)
10 | self.characters.insert(self.cursor.position,
11 | character)
12 | self.cursor.forward()
13 |
14 | def delete(self):
15 | del self.characters[self.cursor.position]
16 |
17 | def save(self):
18 | f = open(self.filename, 'w')
19 | f.write(''.join(self.characters))
20 | f.close()
21 |
22 | @property
23 | def string(self):
24 | return "".join((str(c) for c in self.characters))
25 |
26 |
27 | class Cursor:
28 | def __init__(self, document):
29 | self.document = document
30 | self.position = 0
31 |
32 | def forward(self):
33 | self.position += 1
34 |
35 | def back(self):
36 | self.position -= 1
37 |
38 | def home(self):
39 | while self.document.characters[
40 | self.position-1].character != '\n':
41 | self.position -= 1
42 | if self.position == 0:
43 | # Got to beginning of file before newline
44 | break
45 |
46 | def end(self):
47 | while self.position < len(self.document.characters) and \
48 | self.document.characters[
49 | self.position].character != '\n':
50 | self.position += 1
51 |
52 | class Character:
53 | def __init__(self, character,
54 | bold=False, italic=False, underline=False):
55 | assert len(character) == 1
56 | self.character = character
57 | self.bold = bold
58 | self.italic = italic
59 | self.underline = underline
60 |
61 | def __str__(self):
62 | bold = "*" if self.bold else ''
63 | italic = "/" if self.italic else ''
64 | underline = "_" if self.underline else ''
65 | return bold + italic + underline + self.character
66 |
--------------------------------------------------------------------------------
/Chapter5/zip_processor.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 | import zipfile
4 | from pathlib import Path
5 |
6 |
7 | class ZipProcessor:
8 | def __init__(self, zipname):
9 | self.zipname = zipname
10 | self.temp_directory = Path("unzipped-{}".format(
11 | zipname[:-4]))
12 |
13 | def process_zip(self):
14 | self.unzip_files()
15 | self.process_files()
16 | self.zip_files()
17 |
18 | def unzip_files(self):
19 | self.temp_directory.mkdir()
20 | with zipfile.ZipFile(self.zipname) as zip:
21 | zip.extractall(str(self.temp_directory))
22 |
23 | def zip_files(self):
24 | with zipfile.ZipFile(self.zipname, 'w') as file:
25 | for filename in self.temp_directory.iterdir():
26 | file.write(str(filename), filename.name)
27 | shutil.rmtree(str(self.temp_directory))
28 |
--------------------------------------------------------------------------------
/Chapter5/zip_processor.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter5/zip_processor.pyc
--------------------------------------------------------------------------------
/Chapter6/6_01_empty_object.py:
--------------------------------------------------------------------------------
1 | class MyObject:
2 | pass
3 |
--------------------------------------------------------------------------------
/Chapter6/6_02_pass_tuple_to_function.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | def middle(stock, date):
3 | symbol, current, high, low = stock
4 | return (((high + low) /2), date)
5 |
6 | mid_value, date = middle(("GOOG", 613.30, 625.86, 610.50),
7 | datetime.date(2010, 1, 6))
8 |
--------------------------------------------------------------------------------
/Chapter6/6_03_named_tuple.py:
--------------------------------------------------------------------------------
1 | from collections import namedtuple
2 | Stock = namedtuple("Stock", "symbol current high low")
3 | stock = Stock("FB", 75.00, high=75.03, low=74.90)
4 |
--------------------------------------------------------------------------------
/Chapter6/6_04_dict_stocks.py:
--------------------------------------------------------------------------------
1 | stocks = {"GOOG": (613.30, 625.86, 610.50),
2 | "MSFT": (30.25, 30.70, 30.19)}
3 |
--------------------------------------------------------------------------------
/Chapter6/6_05_random_key_dict.py:
--------------------------------------------------------------------------------
1 | random_keys = {}
2 | random_keys["astring"] = "somestring"
3 | random_keys[5] = "aninteger"
4 | random_keys[25.2] = "floats work too"
5 | random_keys[("abc", 123)] = "so do tuples"
6 |
7 |
8 | class AnObject:
9 | def __init__(self, avalue):
10 | self.avalue = avalue
11 |
12 | my_object = AnObject(14)
13 | random_keys[my_object] = "We can even store objects"
14 | my_object.avalue = 12
15 | try:
16 | random_keys[[1,2,3]] = "we can't store lists though"
17 | except:
18 | print("unable to store list\n")
19 |
20 | for key, value in random_keys.items():
21 | print("{} has value {}".format(key, value))
22 |
--------------------------------------------------------------------------------
/Chapter6/6_06_setdefault_frequency.py:
--------------------------------------------------------------------------------
1 | def letter_frequency(sentence):
2 | frequencies = {}
3 | for letter in sentence:
4 | frequency = frequencies.setdefault(letter, 0)
5 | frequencies[letter] = frequency + 1
6 | return frequencies
7 |
--------------------------------------------------------------------------------
/Chapter6/6_07_defaultdict_frequency.py:
--------------------------------------------------------------------------------
1 | from collections import defaultdict
2 | def letter_frequency(sentence):
3 | frequencies = defaultdict(int)
4 | for letter in sentence:
5 | frequencies[letter] += 1
6 | return frequencies
7 |
--------------------------------------------------------------------------------
/Chapter6/6_08_defaultdict_custom_function.py:
--------------------------------------------------------------------------------
1 | from collections import defaultdict
2 | num_items = 0
3 | def tuple_counter():
4 | global num_items
5 | num_items += 1
6 | return (num_items, [])
7 |
8 | d = defaultdict(tuple_counter)
9 |
--------------------------------------------------------------------------------
/Chapter6/6_09_counter_frequency.py:
--------------------------------------------------------------------------------
1 | from collections import Counter
2 | def letter_frequency(sentence):
3 | return Counter(sentence)
4 |
--------------------------------------------------------------------------------
/Chapter6/6_09_counter_poll.py:
--------------------------------------------------------------------------------
1 | from collections import Counter
2 |
3 | responses = [
4 | "vanilla",
5 | "chocolate",
6 | "vanilla",
7 | "vanilla",
8 | "caramel",
9 | "strawberry",
10 | "vanilla"
11 | ]
12 |
13 | print(
14 | "The children voted for {} ice cream".format(
15 | Counter(responses).most_common(1)[0][0]
16 | )
17 | )
18 |
--------------------------------------------------------------------------------
/Chapter6/6_09_list_tuple_frequency.py:
--------------------------------------------------------------------------------
1 | import string
2 | CHARACTERS = list(string.ascii_letters) + [" "]
3 |
4 | def letter_frequency(sentence):
5 | frequencies = [(c, 0) for c in CHARACTERS]
6 | for letter in sentence:
7 | index = CHARACTERS.index(letter)
8 | frequencies[index] = (letter,frequencies[index][1] + 1)
9 | return frequencies
10 |
--------------------------------------------------------------------------------
/Chapter6/6_10_object_comparison.py:
--------------------------------------------------------------------------------
1 | from functools import total_ordering
2 |
3 | @total_ordering
4 | class WeirdSortee:
5 | def __init__(self, string, number, sort_num):
6 | self.string = string
7 | self.number = number
8 | self.sort_num = sort_num
9 |
10 | def __lt__(self, object):
11 | if self.sort_num:
12 | return self.number < object.number
13 | return self.string < object.string
14 |
15 | def __repr__(self):
16 | return"{}:{}".format(self.string, self.number)
17 |
18 | def __eq__(self, object):
19 | return all((
20 | self.string == object.string,
21 | self.number == object.number,
22 | self.sort_num == object.number
23 | ))
24 |
--------------------------------------------------------------------------------
/Chapter6/6_11_song_artist_set.py:
--------------------------------------------------------------------------------
1 | song_library = [("Phantom Of The Opera", "Sarah Brightman"),
2 | ("Knocking On Heaven's Door", "Guns N' Roses"),
3 | ("Captain Nemo", "Sarah Brightman"),
4 | ("Patterns In The Ivy", "Opeth"),
5 | ("November Rain", "Guns N' Roses"),
6 | ("Beautiful", "Sarah Brightman"),
7 | ("Mal's Song", "Vixy and Tony")]
8 |
9 | artists = set()
10 | for song, artist in song_library:
11 | artists.add(artist)
12 |
13 | print(artists)
14 |
--------------------------------------------------------------------------------
/Chapter6/6_12_set_operations.py:
--------------------------------------------------------------------------------
1 | my_artists = {"Sarah Brightman", "Guns N' Roses",
2 | "Opeth", "Vixy and Tony"}
3 |
4 | auburns_artists = {"Nickelback", "Guns N' Roses",
5 | "Savage Garden"}
6 |
7 | print("All: {}".format(my_artists.union(auburns_artists)))
8 | print("Both: {}".format(auburns_artists.intersection(my_artists)))
9 | print("Either but not both: {}".format(
10 | my_artists.symmetric_difference(auburns_artists)))
11 |
--------------------------------------------------------------------------------
/Chapter6/6_13_set_operations2.py:
--------------------------------------------------------------------------------
1 | my_artists = {"Sarah Brightman", "Guns N' Roses",
2 | "Opeth", "Vixy and Tony"}
3 |
4 | bands = {"Guns N' Roses", "Opeth"}
5 |
6 | print("my_artists is to bands:")
7 | print("issuperset: {}".format(my_artists.issuperset(bands)))
8 | print("issubset: {}".format(my_artists.issubset(bands)))
9 | print("difference: {}".format(my_artists.difference(bands)))
10 | print("*"*20)
11 | print("bands is to my_artists:")
12 | print("issuperset: {}".format(bands.issuperset(my_artists)))
13 | print("issubset: {}".format(bands.issubset(my_artists)))
14 | print("difference: {}".format(bands.difference(my_artists)))
15 |
--------------------------------------------------------------------------------
/Chapter6/6_14_oop_pairs.py:
--------------------------------------------------------------------------------
1 | c = a + b
2 | c = a.add(b)
3 |
4 | l[0] = 5
5 | l.setitem(0, 5)
6 |
7 | d[key] = value
8 | d.setitem(key, value)
9 |
10 | for x in alist:
11 | #do something with x
12 | it = alist.iterator()
13 | while it.has_next():
14 | x = it.next()
15 | #do something with x
16 |
17 |
--------------------------------------------------------------------------------
/Chapter6/6_15_stupid_adding_integer.py:
--------------------------------------------------------------------------------
1 | class SillyInt(int):
2 | def __add__(self, num):
3 | return 0
4 |
--------------------------------------------------------------------------------
/Chapter6/6_16_dictsorted.py:
--------------------------------------------------------------------------------
1 | from collections import KeysView, ItemsView, ValuesView
2 | class DictSorted(dict):
3 | def __new__(*args, **kwargs):
4 | new_dict = dict.__new__(*args, **kwargs)
5 | new_dict.ordered_keys = []
6 | return new_dict
7 |
8 | def __setitem__(self, key, value):
9 | '''self[key] = value syntax'''
10 | if key not in self.ordered_keys:
11 | self.ordered_keys.append(key)
12 | super().__setitem__(key, value)
13 |
14 | def setdefault(self, key, value):
15 | if key not in self.ordered_keys:
16 | self.ordered_keys.append(key)
17 | return super().setdefault(key, value)
18 |
19 | def keys(self):
20 | return KeysView(self)
21 |
22 | def values(self):
23 | return ValuesView(self)
24 |
25 | def items(self):
26 | return ItemsView(self)
27 |
28 | def __iter__(self):
29 | '''for x in self syntax'''
30 | return self.ordered_keys.__iter__()
31 |
--------------------------------------------------------------------------------
/Chapter6/6_17_link_parser.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 | from urllib.parse import urlparse
3 | import re
4 | import sys
5 | LINK_REGEX = re.compile(
6 | "]*href=['\"]([^'\"]+)['\"][^>]*>")
7 |
8 | class LinkCollector:
9 | def __init__(self, url):
10 | self.url = "http://" + urlparse(url).netloc
11 |
12 | def collect_links(self, path="/"):
13 | full_url = self.url + path
14 | page = str(urlopen(full_url).read())
15 | links = LINK_REGEX.findall(page)
16 | print(links)
17 |
18 | if __name__ == "__main__":
19 | LinkCollector(sys.argv[1]).collect_links()
20 |
--------------------------------------------------------------------------------
/Chapter6/6_18_normalize_url.py:
--------------------------------------------------------------------------------
1 | def normalize_url(self, path, link):
2 | if link.startswith("http://"):
3 | return link
4 | elif link.startswith("/"):
5 | return self.url + link
6 | else:
7 | return self.url + path.rpartition('/'
8 | )[0] + '/' + link
9 |
--------------------------------------------------------------------------------
/Chapter6/6_19_visited_links_sets.py:
--------------------------------------------------------------------------------
1 | class LinkCollector:
2 | def __init__(self, url):
3 | self.url = "http://" + urlparse(url).netloc
4 | self.collected_links = set()
5 | self.visited_links = set()
6 |
7 | def collect_links(self, path="/"):
8 | full_url = self.url + path
9 | self.visited_links.add(full_url)
10 | page = str(urlopen(full_url).read())
11 | links = LINK_REGEX.findall(page)
12 | links = {self.normalize_url(path, link
13 | ) for link in links}
14 | self.collected_links = links.union(
15 | self.collected_links)
16 | unvisited_links = links.difference(
17 | self.visited_links)
18 | print(links, self.visited_links,
19 | self.collected_links, unvisited_links)
20 |
21 | def normalize_url(self, path, link):
22 | if link.startswith("http://"):
23 | return link
24 | elif link.startswith("/"):
25 | return self.url + link
26 | else:
27 | return self.url + path.rpartition('/'
28 | )[0] + '/' + link
29 |
--------------------------------------------------------------------------------
/Chapter6/6_20_collect_remaining_links.py:
--------------------------------------------------------------------------------
1 | for link in unvisited_links:
2 | if not link.startswith(self.url):
3 | continue
4 | self.collect_links(urlparse(link).path)
5 |
--------------------------------------------------------------------------------
/Chapter6/6_21_print_collected_links.py:
--------------------------------------------------------------------------------
1 | if __name__ == "__main__":
2 | collector = LinkCollector(sys.argv[1])
3 | collector.collect_links()
4 | for link in collector.collected_links:
5 | print(link)
6 |
--------------------------------------------------------------------------------
/Chapter6/6_22_dict_link_collector.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 | from urllib.parse import urlparse
3 | import re
4 | import sys
5 | LINK_REGEX = re.compile(
6 | "]*href=['\"]([^'\"]+)['\"][^>]*>")
7 |
8 | class LinkCollector:
9 | def __init__(self, url):
10 | self.url = "http://%s" % urlparse(url).netloc
11 | self.collected_links = {}
12 | self.visited_links = set()
13 |
14 | def collect_links(self, path="/"):
15 | full_url = self.url + path
16 | self.visited_links.add(full_url)
17 | page = str(urlopen(full_url).read())
18 | links = LINK_REGEX.findall(page)
19 | links = {self.normalize_url(path, link
20 | ) for link in links}
21 | self.collected_links[full_url] = links
22 | for link in links:
23 | self.collected_links.setdefault(link, set())
24 | unvisited_links = links.difference(
25 | self.visited_links)
26 | for link in unvisited_links:
27 | if link.startswith(self.url):
28 | self.collect_links(urlparse(link).path)
29 |
30 | def normalize_url(self, path, link):
31 | if link.startswith("http://"):
32 | return link
33 | elif link.startswith("/"):
34 | return self.url + link
35 | else:
36 | return self.url + path.rpartition('/'
37 | )[0] + '/' + link
38 |
39 | if __name__ == "__main__":
40 | collector = LinkCollector(sys.argv[1])
41 | collector.collect_links()
42 | for link, item in collector.collected_links.items():
43 | print("{}: {}".format(link, item))
44 |
--------------------------------------------------------------------------------
/Chapter6/6_23_queue_link_collector.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 | from urllib.parse import urlparse
3 | import re
4 | import sys
5 | from queue import Queue
6 | LINK_REGEX = re.compile("]*href=['\"]([^'\"]+)['\"][^>]*>")
7 |
8 |
9 | class LinkCollector:
10 | def __init__(self, url):
11 | self.url = "http://%s" % urlparse(url).netloc
12 | self.collected_links = {}
13 | self.visited_links = set()
14 |
15 | def collect_links(self):
16 | queue = Queue()
17 | queue.put(self.url)
18 | while not queue.empty():
19 | url = queue.get().rstrip('/')
20 | self.visited_links.add(url)
21 | page = str(urlopen(url).read())
22 | links = LINK_REGEX.findall(page)
23 | links = {
24 | self.normalize_url(urlparse(url).path, link)
25 | for link in links
26 | }
27 | self.collected_links[url] = links
28 | for link in links:
29 | self.collected_links.setdefault(link, set())
30 | unvisited_links = links.difference(self.visited_links)
31 | for link in unvisited_links:
32 | if link.startswith(self.url):
33 | queue.put(link)
34 |
35 | def normalize_url(self, path, link):
36 | if link.startswith("http://"):
37 | return link.rstrip('/')
38 | elif link.startswith("/"):
39 | return self.url + link.rstrip('/')
40 | else:
41 | return self.url + path.rpartition('/')[0] + '/' + link.rstrip('/')
42 |
43 | if __name__ == "__main__":
44 | collector = LinkCollector(sys.argv[1])
45 | collector.collect_links()
46 | for link, item in collector.collected_links.items():
47 | print("%s: %s" % (link, item))
48 |
--------------------------------------------------------------------------------
/Chapter6/case_study_serve/blog.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | My real blog is here if you are
4 | interested.
5 |
6 | Here are some links:
7 | The first page
8 | Some contact info
9 | All about my dog!
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Chapter6/case_study_serve/contact.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | I can be contacted in lots of ways. Here are some links:
4 |
5 | My Blog
6 | Home
7 | Contact
8 | Dad's books
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Chapter6/case_study_serve/esme.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | I have a Cavalier King Charles Spaniel named Esme. She's named after Esme (for
4 | Esmerelda) Weatherwax, from Terry Pratchett's Discworld series.
5 |
6 | Here's some links:
7 |
8 | My hobbies
9 | Wikipedia
10 | on Cavaliers
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Chapter6/case_study_serve/hobbies.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | I do yoga, woodworking, skating, writing, and programming.
4 |
5 | My studio
6 | Yoga stuff
7 | The home page
8 | Esme is my hobby too
9 | Recent stuff
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Chapter6/case_study_serve/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Contact us
4 | Blog
5 | My Dog
6 | Some hobbies
7 | Contact AGAIN
8 | Favourite OS
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Chapter6/case_study_serve/yoga.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | I have studied Kung Fu, Tai Chi, Chi Kung, fan, and karate.
4 | Now I practice yoga at Be Luminous.
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Chapter6/link_collector.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 | from urllib.parse import urlparse
3 | import re
4 | import sys
5 | from queue import Queue
6 | LINK_REGEX = re.compile("]*href=['\"]([^'\"]+)['\"][^>]*>")
7 |
8 |
9 | class LinkCollector:
10 | def __init__(self, url):
11 | self.url = "http://%s" % urlparse(url).netloc
12 | self.collected_links = {}
13 | self.visited_links = set()
14 |
15 | def collect_links(self):
16 | queue = Queue()
17 | queue.put(self.url)
18 | while not queue.empty():
19 | url = queue.get().rstrip('/')
20 | self.visited_links.add(url)
21 | page = str(urlopen(url).read())
22 | links = LINK_REGEX.findall(page)
23 | links = {
24 | self.normalize_url(urlparse(url).path, link)
25 | for link in links
26 | }
27 | self.collected_links[url] = links
28 | for link in links:
29 | self.collected_links.setdefault(link, set())
30 | unvisited_links = links.difference(self.visited_links)
31 | for link in unvisited_links:
32 | if link.startswith(self.url):
33 | queue.put(link)
34 |
35 | def normalize_url(self, path, link):
36 | if link.startswith("http://"):
37 | return link.rstrip('/')
38 | elif link.startswith("/"):
39 | return self.url + link.rstrip('/')
40 | else:
41 | return self.url + path.rpartition('/')[0] + '/' + link.rstrip('/')
42 |
43 | if __name__ == "__main__":
44 | collector = LinkCollector(sys.argv[1])
45 | collector.collect_links()
46 | for link, item in collector.collected_links.items():
47 | print("%s: %s" % (link, item))
48 |
--------------------------------------------------------------------------------
/Chapter7/7_01_reversible_objects.py:
--------------------------------------------------------------------------------
1 | normal_list=[1,2,3,4,5]
2 |
3 | class CustomSequence():
4 | def __len__(self):
5 | return 5
6 |
7 | def __getitem__(self, index):
8 | return "x{0}".format(index)
9 |
10 | class FunkyBackwards():
11 | def __reversed__(self):
12 | return "BACKWARDS!"
13 |
14 | for seq in normal_list, CustomSequence(), FunkyBackwards():
15 | print("\n{}: ".format(seq.__class__.__name__), end="")
16 | for item in reversed(seq):
17 | print(item, end=", ")
18 |
--------------------------------------------------------------------------------
/Chapter7/7_02_enumerate_line_numbers.py:
--------------------------------------------------------------------------------
1 | import sys
2 | filename = sys.argv[1]
3 |
4 | with open(filename) as file:
5 | for index, line in enumerate(file):
6 | print("{0}: {1}".format(index+1, line), end='')
7 |
--------------------------------------------------------------------------------
/Chapter7/7_03.1_zip_to_enumerate.py:
--------------------------------------------------------------------------------
1 |
2 | def zip_enumerate(container):
3 | return zip(range(len(container)), container)
4 |
5 | for idx, val in zip_enumerate("hello world"):
6 | print(idx, val)
7 |
--------------------------------------------------------------------------------
/Chapter7/7_03_enumerate_max_min.py:
--------------------------------------------------------------------------------
1 | from operator import itemgetter
2 |
3 | def min_max_indexes(seq):
4 | minimum = min(enumerate(seq), key=itemgetter(1))
5 | maximum = max(enumerate(seq), key=itemgetter(1))
6 | return minimum[0], maximum[0]
7 |
--------------------------------------------------------------------------------
/Chapter7/7_04_tdf_contact.txt:
--------------------------------------------------------------------------------
1 | first last email
2 | john smith jsmith@example.com
3 | jane doan janed@example.com
4 | david neilson dn@example.com
5 |
--------------------------------------------------------------------------------
/Chapter7/7_05_read_file.py:
--------------------------------------------------------------------------------
1 | file = open('filename')
2 | print(file.read())
3 |
--------------------------------------------------------------------------------
/Chapter7/7_05_tdf_processor.py:
--------------------------------------------------------------------------------
1 | import sys
2 | filename = sys.argv[1]
3 |
4 | with open(filename) as file:
5 | header = file.readline().strip().split('\t')
6 | contacts = [
7 | dict(
8 | zip(header, line.strip().split('\t'))
9 | ) for line in file
10 | ]
11 |
12 | for contact in contacts:
13 | print("email: {email} -- {last}, {first}".format(
14 | **contact))
15 |
--------------------------------------------------------------------------------
/Chapter7/7_06_write.py:
--------------------------------------------------------------------------------
1 | contents = "Some file contents"
2 | file = open("filename", "w")
3 | file.write(contents)
4 | file.close()
5 |
--------------------------------------------------------------------------------
/Chapter7/7_07_with.py:
--------------------------------------------------------------------------------
1 | with open('filename') as file:
2 | for line in file:
3 | print(line, end='')
4 |
--------------------------------------------------------------------------------
/Chapter7/7_08_context_manager.py:
--------------------------------------------------------------------------------
1 | class StringJoiner(list):
2 | def __enter__(self):
3 | return self
4 |
5 | def __exit__(self, type, value, tb):
6 | self.result = "".join(self)
7 |
8 | import random, string
9 | with StringJoiner() as joiner:
10 | for i in range(15):
11 | joiner.append(random.choice(string.ascii_letters))
12 |
13 | print(joiner.result)
14 |
--------------------------------------------------------------------------------
/Chapter7/7_18_bad_kw_default.py:
--------------------------------------------------------------------------------
1 | number = 5
2 | def funky_function(number=number):
3 | print(number)
4 |
5 | number=6
6 | funky_function(8)
7 | funky_function()
8 | print(number)
9 |
--------------------------------------------------------------------------------
/Chapter7/7_19_link_downloader.py:
--------------------------------------------------------------------------------
1 | from collections import Iterable
2 |
3 | def get_pages(links):
4 | if not isinstance(links, Iterable) or isinstance(
5 | links, (bytes, str)):
6 | links = [links]
7 | for link in links:
8 | #download the link with urllib
9 | print(link)
10 |
11 |
--------------------------------------------------------------------------------
/Chapter7/7_20_link_downloader_vararg.py:
--------------------------------------------------------------------------------
1 | def get_pages(*links):
2 | for link in links:
3 | #download the link with urllib
4 | print(link)
5 |
6 |
--------------------------------------------------------------------------------
/Chapter7/7_21_kwarg_options.py:
--------------------------------------------------------------------------------
1 | class Options:
2 | default_options = {
3 | 'port': 21,
4 | 'host': 'localhost',
5 | 'username': None,
6 | 'password': None,
7 | 'debug': False,
8 | }
9 | def __init__(self, **kwargs):
10 | self.options = dict(Options.default_options)
11 | self.options.update(kwargs)
12 |
13 | def __getitem__(self, key):
14 | return self.options[key]
15 |
--------------------------------------------------------------------------------
/Chapter7/7_22_all_arguments.py:
--------------------------------------------------------------------------------
1 | import shutil
2 | import os.path
3 | def augmented_move(target_folder, *filenames,
4 | verbose=False, **specific):
5 | '''Move all filenames into the target_folder, allowing
6 | specific treatment of certain files.'''
7 |
8 | def print_verbose(message, filename):
9 | '''print the message only if verbose is enabled'''
10 | if verbose:
11 | print(message.format(filename))
12 |
13 | for filename in filenames:
14 | target_path = os.path.join(target_folder, filename)
15 | if filename in specific:
16 | if specific[filename] == 'ignore':
17 | print_verbose("Ignoring {0}", filename)
18 | elif specific[filename] == 'copy':
19 | print_verbose("Copying {0}", filename)
20 | shutil.copyfile(filename, target_path)
21 | else:
22 | print_verbose("Moving {0}", filename)
23 | shutil.move(filename, target_path)
24 |
--------------------------------------------------------------------------------
/Chapter7/7_23_unpacking_arguments.py:
--------------------------------------------------------------------------------
1 | def show_args(arg1, arg2, arg3="THREE"):
2 | print(arg1, arg2, arg3)
3 |
4 | some_args = range(3)
5 | more_args = {
6 | "arg1": "ONE",
7 | "arg2": "TWO"}
8 |
9 | print("Unpacking a sequence:", end=" ")
10 | show_args(*some_args)
11 | print("Unpacking a dict:", end=" ")
12 | show_args(**more_args)
13 |
--------------------------------------------------------------------------------
/Chapter7/7_24_function_object.py:
--------------------------------------------------------------------------------
1 | def my_function():
2 | print("The Function Was Called")
3 | my_function.description = "A silly function"
4 |
5 | def second_function():
6 | print("The second was called")
7 | second_function.description = "A sillier function."
8 |
9 | def another_function(function):
10 | print("The description:", end=" ")
11 | print(function.description)
12 | print("The name:", end=" ")
13 | print(function.__name__)
14 | print("The class:", end=" ")
15 | print(function.__class__)
16 | print("Now I'll call the function passed in")
17 | function()
18 |
19 | another_function(my_function)
20 | another_function(second_function)
21 |
--------------------------------------------------------------------------------
/Chapter7/7_25_timer.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import time
3 |
4 | class TimedEvent:
5 | def __init__(self, endtime, callback):
6 | self.endtime = endtime
7 | self.callback = callback
8 |
9 | def ready(self):
10 | return self.endtime <= datetime.datetime.now()
11 |
12 | class Timer:
13 | def __init__(self):
14 | self.events = []
15 |
16 | def call_after(self, delay, callback):
17 | end_time = datetime.datetime.now() + \
18 | datetime.timedelta(seconds=delay)
19 |
20 | self.events.append(TimedEvent(end_time, callback))
21 |
22 | def run(self):
23 | while True:
24 | ready_events = (e for e in self.events if e.ready())
25 | for event in ready_events:
26 | event.callback(self)
27 | self.events.remove(event)
28 | time.sleep(0.5)
29 |
--------------------------------------------------------------------------------
/Chapter7/7_26_timer_test.py:
--------------------------------------------------------------------------------
1 | from timer import Timer
2 | import datetime
3 |
4 | def format_time(message, *args):
5 | now = datetime.datetime.now().strftime("%I:%M:%S")
6 | print(message.format(*args, now=now))
7 |
8 | def one(timer):
9 | format_time("{now}: Called One")
10 |
11 | def two(timer):
12 | format_time("{now}: Called Two")
13 |
14 | def three(timer):
15 | format_time("{now}: Called Three")
16 |
17 | class Repeater:
18 | def __init__(self):
19 | self.count = 0
20 | def repeater(self, timer):
21 | format_time("{now}: repeat {0}", self.count)
22 | self.count += 1
23 | timer.call_after(5, self.repeater)
24 |
25 | timer = Timer()
26 | timer.call_after(1, one)
27 | timer.call_after(2, one)
28 | timer.call_after(2, two)
29 | timer.call_after(4, two)
30 | timer.call_after(3, three)
31 | timer.call_after(6, three)
32 | repeater = Repeater()
33 | timer.call_after(5, repeater.repeater)
34 | format_time("{now}: Starting")
35 | timer.run()
36 |
--------------------------------------------------------------------------------
/Chapter7/7_27_add_function_to_object.py:
--------------------------------------------------------------------------------
1 | class A:
2 | def print(self):
3 | print("my class is A")
4 |
5 | def fake_print():
6 | print("my class is not A")
7 |
8 | a = A()
9 | a.print()
10 | a.print = fake_print
11 | a.print()
12 |
--------------------------------------------------------------------------------
/Chapter7/7_28_callable_repeat.py:
--------------------------------------------------------------------------------
1 | from timer import Timer
2 | import datetime
3 |
4 | def format_time(message, *args):
5 | now = datetime.datetime.now().strftime("%I:%M:%S")
6 | print(message.format(*args, now=now))
7 |
8 | class Repeater:
9 | def __init__(self):
10 | self.count = 0
11 | def __call__(self, timer):
12 | format_time("{now}: repeat {0}", self.count)
13 | self.count += 1
14 | timer.call_after(5, self)
15 |
16 | timer = Timer()
17 | timer.call_after(5, Repeater())
18 | format_time("{now}: Starting")
19 | timer.run()
20 |
--------------------------------------------------------------------------------
/Chapter7/7_29_send_email.py:
--------------------------------------------------------------------------------
1 | import smtplib
2 | from email.mime.text import MIMEText
3 |
4 | def send_email(subject, message, from_addr, *to_addrs,
5 | host="localhost", port=1025, **headers):
6 |
7 | email = MIMEText(message)
8 | email['Subject'] = subject
9 | email['From'] = from_addr
10 | for header, value in headers.items():
11 | email[header] = value
12 |
13 | sender = smtplib.SMTP(host, port)
14 | for addr in to_addrs:
15 | del email['To']
16 | email['To'] = addr
17 | sender.sendmail(from_addr, addr, email.as_string())
18 | sender.quit()
19 |
--------------------------------------------------------------------------------
/Chapter7/7_30_send_email_dict_headers.py:
--------------------------------------------------------------------------------
1 | import smtplib
2 | from email.mime.text import MIMEText
3 |
4 | def send_email(subject, message, from_addr, *to_addrs,
5 | host="localhost", port=1025, headers=None):
6 |
7 | headers = {} if headers is None else headers
8 |
9 | email = MIMEText(message)
10 | email['Subject'] = subject
11 | email['From'] = from_addr
12 | for header, value in headers.items():
13 | email[header] = value
14 |
15 | sender = smtplib.SMTP(host, port)
16 | for addr in to_addrs:
17 | del email['To']
18 | email['To'] = addr
19 | sender.sendmail(from_addr, addr, email.as_string())
20 | sender.quit()
21 |
--------------------------------------------------------------------------------
/Chapter7/7_32_mailing_list_defaultdict_set.py:
--------------------------------------------------------------------------------
1 |
2 | class MailingList:
3 | '''Manage groups of e-mail addresses for sending e-mails.'''
4 |
5 | def __init__(self):
6 | self.email_map = defaultdict(set)
7 |
8 | def add_to_group(self, email, group):
9 | self.email_map[email].add(group)
10 |
--------------------------------------------------------------------------------
/Chapter7/7_33_mailing_list_get_emails.py:
--------------------------------------------------------------------------------
1 |
2 | def emails_in_groups(self, *groups):
3 | groups = set(groups)
4 | emails = set()
5 | for e, g in self.email_map.items():
6 | if g & groups:
7 | emails.add(e)
8 | return emails
9 |
--------------------------------------------------------------------------------
/Chapter7/7_34_send_mailing.py:
--------------------------------------------------------------------------------
1 |
2 | def send_mailing(self, subject, message, from_addr,
3 | *groups, headers=None):
4 | emails = self.emails_in_groups(*groups)
5 | send_email(subject, message, from_addr,
6 | *emails, headers=headers)
7 |
--------------------------------------------------------------------------------
/Chapter7/7_35_load_save.py:
--------------------------------------------------------------------------------
1 | def save(self):
2 | with open(self.data_file, 'w') as file:
3 | for email, groups in self.email_map.items():
4 | file.write(
5 | '{} {}\n'.format(email, ','.join(groups))
6 | )
7 |
8 | def load(self):
9 | self.email_map = defaultdict(set)
10 | try:
11 | with open(self.data_file) as file:
12 | for line in file:
13 | email, groups = line.strip().split(' ')
14 | groups = set(groups.split(','))
15 | self.email_map[email] = groups
16 | except IOError:
17 | pass
18 |
--------------------------------------------------------------------------------
/Chapter7/7_36_enter_exit.py:
--------------------------------------------------------------------------------
1 | def __enter__(self):
2 | self.load()
3 | return self
4 |
5 | def __exit__(self, type, value, tb):
6 | self.save()
7 |
--------------------------------------------------------------------------------
/Chapter7/__pycache__/timer.cpython-34.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter7/__pycache__/timer.cpython-34.pyc
--------------------------------------------------------------------------------
/Chapter7/addresses.db:
--------------------------------------------------------------------------------
1 | friend1@example.com friends
2 | friend2@example.com friends
3 | family1@example.com family,friends
4 |
--------------------------------------------------------------------------------
/Chapter7/mailing_list.py:
--------------------------------------------------------------------------------
1 | import smtplib
2 | from email.mime.text import MIMEText
3 | from collections import defaultdict
4 |
5 |
6 | def send_email(subject, message, from_addr, *to_addrs,
7 | host="localhost", port=1025, headers=None):
8 |
9 | headers = {} if headers is None else headers
10 |
11 | email = MIMEText(message)
12 | email['Subject'] = subject
13 | email['From'] = from_addr
14 | for header, value in headers.items():
15 | email[header] = value
16 |
17 | sender = smtplib.SMTP(host, port)
18 | for addr in to_addrs:
19 | del email['To']
20 | email['To'] = addr
21 | sender.sendmail(from_addr, addr, email.as_string())
22 | sender.quit()
23 |
24 |
25 | class MailingList:
26 | '''Manage groups of e-mail addresses for sending e-mails.'''
27 |
28 | def __init__(self, data_file):
29 | self.data_file = data_file
30 | self.email_map = defaultdict(set)
31 |
32 | def add_to_group(self, email, group):
33 | self.email_map[email].add(group)
34 |
35 | def emails_in_groups(self, *groups):
36 | groups = set(groups)
37 | emails = set()
38 | for e, g in self.email_map.items():
39 | if g & groups:
40 | emails.add(e)
41 | return emails
42 |
43 | def send_mailing(self, subject, message, from_addr,
44 | *groups, headers=None):
45 | emails = self.emails_in_groups(*groups)
46 | send_email(subject, message, from_addr,
47 | *emails, headers=headers)
48 |
49 | def save(self):
50 | with open(self.data_file, 'w') as file:
51 | for email, groups in self.email_map.items():
52 | file.write(
53 | '{} {}\n'.format(email, ','.join(groups))
54 | )
55 |
56 | def load(self):
57 | self.email_map = defaultdict(set)
58 | try:
59 | with open(self.data_file) as file:
60 | for line in file:
61 | email, groups = line.strip().split(' ')
62 | groups = set(groups.split(','))
63 | self.email_map[email] = groups
64 | except IOError:
65 | pass
66 |
67 | def __enter__(self):
68 | self.load()
69 | return self
70 |
71 | def __exit__(self, type, value, tb):
72 | self.save()
73 |
74 |
--------------------------------------------------------------------------------
/Chapter7/timer.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import time
3 |
4 | class TimedEvent:
5 | def __init__(self, endtime, callback):
6 | self.endtime = endtime
7 | self.callback = callback
8 |
9 | def ready(self):
10 | return self.endtime <= datetime.datetime.now()
11 |
12 | class Timer:
13 | def __init__(self):
14 | self.events = []
15 |
16 | def call_after(self, delay, callback):
17 | end_time = datetime.datetime.now() + \
18 | datetime.timedelta(seconds=delay)
19 |
20 | self.events.append(TimedEvent(end_time, callback))
21 |
22 | def run(self):
23 | while True:
24 | ready_events = (e for e in self.events if e.ready())
25 | for event in ready_events:
26 | event.callback(self)
27 | self.events.remove(event)
28 | time.sleep(0.5)
29 |
--------------------------------------------------------------------------------
/Chapter7/timer.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/5add8c66ccef17baad97140fcdb142bfa5337085/Chapter7/timer.pyc
--------------------------------------------------------------------------------
/Chapter8/8_01_string_creation.py:
--------------------------------------------------------------------------------
1 | a = "hello"
2 | b = 'world'
3 | c = '''a multiple
4 | line string'''
5 | d = """More
6 | multiple"""
7 | e = ("Three " "Strings "
8 | "Together")
9 |
--------------------------------------------------------------------------------
/Chapter8/8_02_format_empty.py:
--------------------------------------------------------------------------------
1 | template = "Hello {}, you are currently {}."
2 | print(template.format('Dusty', 'writing'))
3 |
--------------------------------------------------------------------------------
/Chapter8/8_03_format_position.py:
--------------------------------------------------------------------------------
1 | template = "Hello {0}, you are {1}. Your name is {0}."
2 | print(template.format('Dusty', 'writing'))
3 |
--------------------------------------------------------------------------------
/Chapter8/8_04_format_some_positions_broken.py:
--------------------------------------------------------------------------------
1 | template = "Hello {}, you are {}. Your name is {0}."
2 | print(template.format('Dusty', 'writing'))
3 |
--------------------------------------------------------------------------------
/Chapter8/8_05_brace_escape.py:
--------------------------------------------------------------------------------
1 | template = """
2 | public class {0} {{
3 | public static void main(String[] args) {{
4 | System.out.println("{1}");
5 | }}
6 | }}"""
7 |
8 | print(template.format("MyClass", "print('hello world')"));
9 |
--------------------------------------------------------------------------------
/Chapter8/8_06_format_kw_args.py:
--------------------------------------------------------------------------------
1 | template = """
2 | From: <{from_email}>
3 | To: <{to_email}>
4 | Subject: {subject}
5 | {message}"""
6 | print(template.format(
7 | from_email = "a@example.com",
8 | to_email = "b@example.com",
9 | message = "Here's some mail for you. "
10 | " Hope you enjoy the message!",
11 | subject = "You have mail!"
12 | ))
13 |
14 |
--------------------------------------------------------------------------------
/Chapter8/8_07_unlabelled_kw.py:
--------------------------------------------------------------------------------
1 | print("{} {label} {}".format("x", "y", label="z"))
2 |
--------------------------------------------------------------------------------
/Chapter8/8_08_tuple_dict_format.py:
--------------------------------------------------------------------------------
1 | emails = ("a@example.com", "b@example.com")
2 | message = {
3 | 'subject': "You Have Mail!",
4 | 'message': "Here's some mail for you!"
5 | }
6 | template = """
7 | From: <{0[0]}>
8 | To: <{0[1]}>
9 | Subject: {message[subject]}
10 | {message[message]}"""
11 | print(template.format(emails, message=message))
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Chapter8/8_09_tuple_in_dict_format.py:
--------------------------------------------------------------------------------
1 | emails = ("a@example.com", "b@example.com")
2 | message = {
3 | 'emails': emails,
4 | 'subject': "You Have Mail!",
5 | 'message': "Here's some mail for you!"
6 | }
7 | template = """
8 | From: <{0[emails][0]}>
9 | To: <{0[emails][1]}>
10 | Subject: {0[subject]}
11 | {0[message]}"""
12 | print(template.format(message))
13 |
--------------------------------------------------------------------------------
/Chapter8/8_10_object_formatting.py:
--------------------------------------------------------------------------------
1 | class EMail:
2 | def __init__(self, from_addr, to_addr, subject, message):
3 | self.from_addr = from_addr
4 | self.to_addr = to_addr
5 | self.subject = subject
6 | self.message = message
7 |
8 | email = EMail("a@example.com", "b@example.com",
9 | "You Have Mail!",
10 | "Here's some mail for you!")
11 |
12 | template = """
13 | From: <{0.from_addr}>
14 | To: <{0.to_addr}>
15 | Subject: {0.subject}
16 |
17 | {0.message}"""
18 | print(template.format(email))
19 |
--------------------------------------------------------------------------------
/Chapter8/8_11_no_format.py:
--------------------------------------------------------------------------------
1 | subtotal = 12.32
2 | tax = subtotal * 0.07
3 | total = subtotal + tax
4 |
5 | print("Sub: ${0} Tax: ${1} Total: ${total}".format(
6 | subtotal, tax, total=total))
7 |
--------------------------------------------------------------------------------
/Chapter8/8_12_currency_format.py:
--------------------------------------------------------------------------------
1 | subtotal = 12.32
2 | tax = subtotal * 0.07
3 | total = subtotal + tax
4 |
5 | print("Sub: ${0:0.2f} Tax: ${1:0.2f} "
6 | "Total: ${total:0.2f}".format(
7 | subtotal, tax, total=total))
8 |
--------------------------------------------------------------------------------
/Chapter8/8_13_tabular.py:
--------------------------------------------------------------------------------
1 | orders = [('burger', 2, 5),
2 | ('fries', 3.5, 1),
3 | ('cola', 1.75, 3)]
4 |
5 | print("PRODUCT QUANTITY PRICE SUBTOTAL")
6 | for product, price, quantity in orders:
7 | subtotal = price * quantity
8 | print("{0:10s}{1: ^9d} ${2: <8.2f}${3: >7.2f}".format(
9 | product, quantity, price, subtotal))
10 |
11 |
--------------------------------------------------------------------------------
/Chapter8/8_14_format_datetime.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | print("{0:%Y-%m-%d %I:%M%p }".format(
3 | datetime.datetime.now()))
4 |
5 |
--------------------------------------------------------------------------------
/Chapter8/8_15_encode_bytes.py:
--------------------------------------------------------------------------------
1 | characters = b'\x63\x6c\x69\x63\x68\xe9'
2 | print(characters)
3 | print(characters.decode("latin-1"))
4 |
--------------------------------------------------------------------------------
/Chapter8/8_16_decode_unicode.py:
--------------------------------------------------------------------------------
1 | characters = "cliché"
2 | print(characters.encode("UTF-8"))
3 | print(characters.encode("latin-1"))
4 | print(characters.encode("CP437"))
5 | print(characters.encode("ascii"))
6 |
--------------------------------------------------------------------------------
/Chapter8/8_17_bytearray_replace.py:
--------------------------------------------------------------------------------
1 | b = bytearray(b"abcdefgh")
2 | b[4:6] = b"\x15\xa3"
3 | print(b)
4 |
--------------------------------------------------------------------------------
/Chapter8/8_18_bytearray_index.py:
--------------------------------------------------------------------------------
1 | b = bytearray(b'abcdef')
2 | b[3] = ord(b'g')
3 | b[4] = 68
4 | print(b)
5 |
--------------------------------------------------------------------------------
/Chapter8/8_23.1_basic_regex.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | search_string = "hello world"
4 | pattern = "hello world"
5 |
6 | match = re.match(pattern, search_string)
7 |
8 | if match:
9 | print("regex matches")
10 |
--------------------------------------------------------------------------------
/Chapter8/8_23.2_regex_generic.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import re
3 |
4 | pattern = sys.argv[1]
5 | search_string = sys.argv[2]
6 | match = re.match(pattern, search_string)
7 |
8 | if match:
9 | template = "'{}' matches pattern '{}'"
10 | else:
11 | template = "'{}' does not match pattern '{}'"
12 |
13 | print(template.format(search_string, pattern))
14 |
--------------------------------------------------------------------------------
/Chapter8/8_23.3_match_group.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | pattern = "^[a-zA-Z.]+@([a-z.]*\.[a-z]+)$"
4 | search_string = "some.user@example.com"
5 | match = re.match(pattern, search_string)
6 |
7 | if match:
8 | domain = match.groups()[0]
9 | print(domain)
10 |
--------------------------------------------------------------------------------
/Chapter8/8_23_stringio.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | from io import StringIO, BytesIO
3 | source_file = StringIO("an oft-repeated cliché")
4 | dest_file = BytesIO()
5 |
6 | char = source_file.read(1)
7 | while char:
8 | dest_file.write(char.encode("ascii", "replace"))
9 | char = source_file.read(1)
10 |
11 | print(dest_file.getvalue())
12 |
13 |
--------------------------------------------------------------------------------
/Chapter8/8_24_basic_pickling.py:
--------------------------------------------------------------------------------
1 | import pickle
2 |
3 | some_data = ["a list", "containing", 5,
4 | "values including another list",
5 | ["inner", "list"]]
6 |
7 | with open("pickled_list", 'wb') as file:
8 | pickle.dump(some_data, file)
9 |
10 | with open("pickled_list", 'rb') as file:
11 | loaded_data = pickle.load(file)
12 |
13 | print(loaded_data)
14 | assert loaded_data == some_data
15 |
--------------------------------------------------------------------------------
/Chapter8/8_25_state_pickling.py:
--------------------------------------------------------------------------------
1 | from threading import Timer
2 | import datetime
3 | from urllib.request import urlopen
4 |
5 | class UpdatedURL:
6 | def __init__(self, url):
7 | self.url = url
8 | self.contents = ''
9 | self.last_updated = None
10 | self.update()
11 |
12 | def update(self):
13 | self.contents = urlopen(self.url).read()
14 | self.last_updated = datetime.datetime.now()
15 | self.schedule()
16 |
17 | def schedule(self):
18 | self.timer = Timer(3600, self.update)
19 | self.timer.setDaemon(True)
20 | self.timer.start()
21 |
22 | def __getstate__(self):
23 | new_state = self.__dict__.copy()
24 | if 'timer' in new_state:
25 | del new_state['timer']
26 | return new_state
27 |
28 | def __setstate__(self, data):
29 | self.__dict__ = data
30 | self.schedule()
31 |
--------------------------------------------------------------------------------
/Chapter8/8_26_json_objects.py:
--------------------------------------------------------------------------------
1 | class Contact:
2 | def __init__(self, first, last):
3 | self.first = first
4 | self.last = last
5 |
6 | @property
7 | def full_name(self):
8 | return("{} {}".format(self.first, self.last))
9 |
10 |
11 | import json
12 | class ContactEncoder(json.JSONEncoder):
13 | def default(self, obj):
14 | if isinstance(obj, Contact):
15 | return {'is_contact': True,
16 | 'first': obj.first,
17 | 'last': obj.last,
18 | 'full': obj.full_name}
19 | return super().default(obj)
20 |
21 | def decode_contact(dic):
22 | if dic.get('is_contact'):
23 | return Contact(dic['first'], dic['last'])
24 | else:
25 | return dic
26 |
--------------------------------------------------------------------------------
/Chapter8/8_27_template_boilerplate.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sys
3 | import json
4 | from pathlib import Path
5 |
6 | DIRECTIVE_RE = re.compile(
7 | r'/\*\*\s*(include|variable|loopover|endloop|loopvar)'
8 | r'\s*([^ *]*)\s*\*\*/')
9 |
10 |
11 | class TemplateEngine:
12 | def __init__(self, infilename, outfilename, contextfilename):
13 | self.template = open(infilename).read()
14 | self.working_dir = Path(infilename).absolute().parent
15 | self.pos = 0
16 | self.outfile = open(outfilename, 'w')
17 | with open(contextfilename) as contextfile:
18 | self.context = json.load(contextfile)
19 |
20 | def process(self):
21 | print("PROCESSING...")
22 |
23 |
24 | if __name__ == '__main__':
25 | infilename, outfilename, contextfilename = sys.argv[1:]
26 | engine = TemplateEngine(infilename, outfilename, contextfilename)
27 | engine.process()
28 |
--------------------------------------------------------------------------------
/Chapter8/8_28_template_process.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sys
3 | import json
4 | from pathlib import Path
5 |
6 | DIRECTIVE_RE = re.compile(
7 | r'/\*\*\s*(include|variable|loopover|endloop|loopvar)'
8 | r'\s*([^ *]*)\s*\*\*/')
9 |
10 |
11 | class TemplateEngine:
12 | def __init__(self, infilename, outfilename, contextfilename):
13 | self.template = open(infilename).read()
14 | self.working_dir = Path(infilename).absolute().parent
15 | self.pos = 0
16 | self.outfile = open(outfilename, 'w')
17 | with open(contextfilename) as contextfile:
18 | self.context = json.load(contextfile)
19 |
20 | def process(self):
21 | match = DIRECTIVE_RE.search(self.template, pos=self.pos)
22 | while match:
23 | self.outfile.write(self.template[self.pos:match.start()])
24 | directive, argument = match.groups()
25 | self.pos = match.end()
26 | match = DIRECTIVE_RE.search(self.template, pos=self.pos)
27 | self.outfile.write(self.template[self.pos:])
28 |
29 | if __name__ == '__main__':
30 | infilename, outfilename, contextfilename = sys.argv[1:]
31 | engine = TemplateEngine(infilename, outfilename, contextfilename)
32 | engine.process()
33 |
--------------------------------------------------------------------------------
/Chapter8/8_29_template_processer_complete.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sys
3 | import json
4 | from pathlib import Path
5 |
6 | DIRECTIVE_RE = re.compile(
7 | r'/\*\*\s*(include|variable|loopover|endloop|loopvar)'
8 | r'\s*([^ *]*)\s*\*\*/')
9 |
10 |
11 | class TemplateEngine:
12 | def __init__(self, infilename, outfilename, contextfilename):
13 | self.template = open(infilename).read()
14 | self.working_dir = Path(infilename).absolute().parent
15 | self.pos = 0
16 | self.outfile = open(outfilename, 'w')
17 | with open(contextfilename) as contextfile:
18 | self.context = json.load(contextfile)
19 |
20 | def process(self):
21 | match = DIRECTIVE_RE.search(self.template, pos=self.pos)
22 | while match:
23 | self.outfile.write(self.template[self.pos:match.start()])
24 | directive, argument = match.groups()
25 | method_name = 'process_{}'.format(directive)
26 | getattr(self, method_name)(match, argument)
27 | match = DIRECTIVE_RE.search(self.template, pos=self.pos)
28 | self.outfile.write(self.template[self.pos:])
29 |
30 | def process_include(self, match, argument):
31 | with (self.working_dir / argument).open() as includefile:
32 | self.outfile.write(includefile.read())
33 | self.pos = match.end()
34 |
35 | def process_variable(self, match, argument):
36 | self.outfile.write(self.context.get(argument, ''))
37 | self.pos = match.end()
38 |
39 | def process_loopover(self, match, argument):
40 | self.loop_index = 0
41 | self.loop_list = self.context.get(argument, [])
42 | self.pos = self.loop_pos = match.end()
43 |
44 | def process_loopvar(self, match, argument):
45 | self.outfile.write(self.loop_list[self.loop_index])
46 | self.pos = match.end()
47 |
48 | def process_endloop(self, match, argument):
49 | self.loop_index += 1
50 | if self.loop_index >= len(self.loop_list):
51 | self.pos = match.end()
52 | del self.loop_index
53 | del self.loop_list
54 | del self.loop_pos
55 | else:
56 | self.pos = self.loop_pos
57 |
58 | if __name__ == '__main__':
59 | infilename, outfilename, contextfilename = sys.argv[1:]
60 | engine = TemplateEngine(infilename, outfilename, contextfilename)
61 | engine.process()
62 |
--------------------------------------------------------------------------------
/Chapter8/case_study_input/context.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Dusty",
3 | "book_list": [
4 | "Thief Of Time",
5 | "The Thief",
6 | "Snow Crash",
7 | "Lathe Of Heaven"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter8/case_study_input/footer.html:
--------------------------------------------------------------------------------
1 |