diff --git a/.gitignore b/.gitignore index 2333e67..6ba95c4 100644 --- a/.gitignore +++ b/.gitignore @@ -126,3 +126,5 @@ x86/ bld/ file.json dataSources.local.xml + +.idea/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e960a6a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md. + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/apps/net/Ch6_Web/bin/Debug/net8.0/Ch6_Web.dll", + "args": [], + "cwd": "${workspaceFolder}/apps/net/Ch6_Web", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..a151094 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/apps/net/net.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/apps/net/net.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/apps/net/net.sln" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/apps/net/Ch3_Lang/Ch3_Lang.csproj b/apps/net/Ch3_Lang/Ch3_Lang.csproj index 5a59ec4..0e3cc8f 100644 --- a/apps/net/Ch3_Lang/Ch3_Lang.csproj +++ b/apps/net/Ch3_Lang/Ch3_Lang.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp2.1 + net8.0 - + diff --git a/apps/net/Ch4_OOP/Ch4_OOP.csproj b/apps/net/Ch4_OOP/Ch4_OOP.csproj index 898ad85..5670ae8 100644 --- a/apps/net/Ch4_OOP/Ch4_OOP.csproj +++ b/apps/net/Ch4_OOP/Ch4_OOP.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + net8.0 diff --git a/apps/net/Ch5_nuget/Ch5_nuget.csproj b/apps/net/Ch5_nuget/Ch5_nuget.csproj index ada5ca1..bfcc8a2 100644 --- a/apps/net/Ch5_nuget/Ch5_nuget.csproj +++ b/apps/net/Ch5_nuget/Ch5_nuget.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + net8.0 diff --git a/apps/net/Ch6_Web/Ch6_Web.csproj b/apps/net/Ch6_Web/Ch6_Web.csproj index bb9ffce..c255d1e 100644 --- a/apps/net/Ch6_Web/Ch6_Web.csproj +++ b/apps/net/Ch6_Web/Ch6_Web.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0 + net8.0 diff --git a/apps/net/Ch7_Db/Ch7_Db.csproj b/apps/net/Ch7_Db/Ch7_Db.csproj index 01759ca..4bd60f4 100644 --- a/apps/net/Ch7_Db/Ch7_Db.csproj +++ b/apps/net/Ch7_Db/Ch7_Db.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0 + net8.0 @@ -30,7 +30,7 @@ - - + + diff --git a/apps/net/Ch8_Testing/Ch8_Testing.csproj b/apps/net/Ch8_Testing/Ch8_Testing.csproj index 9a9abc2..2d55ab6 100644 --- a/apps/net/Ch8_Testing/Ch8_Testing.csproj +++ b/apps/net/Ch8_Testing/Ch8_Testing.csproj @@ -1,19 +1,19 @@ - netcoreapp3.0 + net8.0 false - - - - runtime; build; native; contentfiles; analyzers; buildtransitive + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/apps/net/Ch8_Testing_App/Ch8_Testing_App.csproj b/apps/net/Ch8_Testing_App/Ch8_Testing_App.csproj index a1ed464..7282f46 100644 --- a/apps/net/Ch8_Testing_App/Ch8_Testing_App.csproj +++ b/apps/net/Ch8_Testing_App/Ch8_Testing_App.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + net8.0 diff --git a/apps/net/Ch9_Async/Ch9_Async.csproj b/apps/net/Ch9_Async/Ch9_Async.csproj index e091d2b..b89bed3 100644 --- a/apps/net/Ch9_Async/Ch9_Async.csproj +++ b/apps/net/Ch9_Async/Ch9_Async.csproj @@ -1,55 +1,14 @@ - - - - Debug - AnyCPU - {82E0B15C-BE72-4F3D-97EB-DA3736244FC4} - Exe - Ch9_Async - Ch8_Async - v4.7.2 - true - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - true - MSB3276 - - - true - bin\Release - prompt - 4 - true - - - - - ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll - - - ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll - - - - - ..\packages\HtmlAgilityPack.1.11.16\lib\Net45\HtmlAgilityPack.dll - - - - - - - - - - - - - \ No newline at end of file + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/apps/net/Ch9_Async/Program.cs b/apps/net/Ch9_Async/Program.cs index 9719381..a6c4cfe 100644 --- a/apps/net/Ch9_Async/Program.cs +++ b/apps/net/Ch9_Async/Program.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; +using System.Diagnostics; using System.Net; -using System.Net.Http; -using System.Threading.Tasks; using HtmlAgilityPack; namespace Ch9_Async diff --git a/apps/net/Ch9_Async/Properties/AssemblyInfo.cs b/apps/net/Ch9_Async/Properties/AssemblyInfo.cs deleted file mode 100644 index bef6021..0000000 --- a/apps/net/Ch9_Async/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle("Ch8_Async")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("PDX Web Properties, LLC")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] diff --git a/apps/net/Ch9_Async/packages.config b/apps/net/Ch9_Async/packages.config deleted file mode 100644 index 2ba22b5..0000000 --- a/apps/net/Ch9_Async/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/apps/net/net.sln b/apps/net/net.sln index f42009a..cb22f1d 100644 --- a/apps/net/net.sln +++ b/apps/net/net.sln @@ -17,7 +17,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ch8_Testing", "Ch8_Testing\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ch8_Testing_App", "Ch8_Testing_App\Ch8_Testing_App.csproj", "{DC35108D-5189-4BC2-8303-5B4799B6A625}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ch9_Async", "Ch9_Async\Ch9_Async.csproj", "{82E0B15C-BE72-4F3D-97EB-DA3736244FC4}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ch9_Async", "Ch9_Async\Ch9_Async.csproj", "{F5C28307-BF7E-419E-97FC-26CC76ECFEF7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -53,10 +53,10 @@ Global {DC35108D-5189-4BC2-8303-5B4799B6A625}.Debug|Any CPU.Build.0 = Debug|Any CPU {DC35108D-5189-4BC2-8303-5B4799B6A625}.Release|Any CPU.ActiveCfg = Release|Any CPU {DC35108D-5189-4BC2-8303-5B4799B6A625}.Release|Any CPU.Build.0 = Release|Any CPU - {82E0B15C-BE72-4F3D-97EB-DA3736244FC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {82E0B15C-BE72-4F3D-97EB-DA3736244FC4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {82E0B15C-BE72-4F3D-97EB-DA3736244FC4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {82E0B15C-BE72-4F3D-97EB-DA3736244FC4}.Release|Any CPU.Build.0 = Release|Any CPU + {F5C28307-BF7E-419E-97FC-26CC76ECFEF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5C28307-BF7E-419E-97FC-26CC76ECFEF7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5C28307-BF7E-419E-97FC-26CC76ECFEF7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5C28307-BF7E-419E-97FC-26CC76ECFEF7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/apps/py/ch03_lang/L01_structure.py b/apps/py/ch03_lang/L01_structure.py index 4e2d546..4acdfcd 100644 --- a/apps/py/ch03_lang/L01_structure.py +++ b/apps/py/ch03_lang/L01_structure.py @@ -1,14 +1,14 @@ def main(): - name = input("What is your name? ") + name = input('What is your name? ') some_method(name) def some_method(name): if name.strip().lower() == 'michael': - print("Hello old friend!") + print('Hello old friend!') else: - print(f"Nice to meet you {name}.") - print("My name is Python!") + print(f'Nice to meet you {name}.') + print('My name is Python!') if __name__ == '__main__': diff --git a/apps/py/ch03_lang/L02_iteration.py b/apps/py/ch03_lang/L02_iteration.py index 9c69018..7d410f5 100644 --- a/apps/py/ch03_lang/L02_iteration.py +++ b/apps/py/ch03_lang/L02_iteration.py @@ -1,5 +1,5 @@ def main(): - print("Python iteration demo") + print('Python iteration demo') # while True: # name = input("What is your name? ") @@ -10,16 +10,16 @@ def main(): nums = [1, 5, 8, 10, 7, 2] # <-- List for n in nums: - print(f"The next number is {n}.") + print(f'The next number is {n}.') print() for idx, n in enumerate(nums, start=1): - print(f"The {idx}th number is {n}.") + print(f'The {idx}th number is {n}.') # NO for (i=0; i < len(nums); i++) print() for _ in range(1, 6): - print("This time!") + print('This time!') if __name__ == '__main__': diff --git a/apps/py/ch03_lang/L03_function_basics.py b/apps/py/ch03_lang/L03_function_basics.py index 1fb9f9d..eb0bd13 100644 --- a/apps/py/ch03_lang/L03_function_basics.py +++ b/apps/py/ch03_lang/L03_function_basics.py @@ -18,7 +18,7 @@ def main(): if evaluate_guess(guess, the_number): break - print(f"You got the number in {count} attempts. Thanks for playing, bye!") + print(f'You got the number in {count} attempts. Thanks for playing, bye!') def evaluate_guess(guess, number): @@ -35,14 +35,14 @@ def evaluate_guess(guess, number): def get_guess(): val = None try: - text = input("What number am I thinking of? ") + text = input('What number am I thinking of? ') val = int(text) if val < 1 or 100 < val: - print(f"{val} is not between 1 and 100.") + print(f'{val} is not between 1 and 100.') return None return val except: - print(f"{val} is not an integer!") + print(f'{val} is not an integer!') return None @@ -54,7 +54,7 @@ def show_header(): print('-------------------------------------------') print() print("I'm thinking of a number between 1 & 100. ") - print("How many steps can you guess it in?") + print('How many steps can you guess it in?') print() # implicit diff --git a/apps/py/ch03_lang/L04_function_args.py b/apps/py/ch03_lang/L04_function_args.py index 2a35ba0..934bce5 100644 --- a/apps/py/ch03_lang/L04_function_args.py +++ b/apps/py/ch03_lang/L04_function_args.py @@ -1,22 +1,22 @@ def main(): - print("say_hello()") + print('say_hello()') say_hello() print() - print("say_hello(name)") - say_hello("Zoe") + print('say_hello(name)') + say_hello('Zoe') print() - print("say_hello(name, times)") - say_hello("Zoe", 5) + print('say_hello(name, times)') + say_hello('Zoe', 5) print() - print("say_hello(name, times, 1, 2, 3, 4)") - say_hello("Zoe", 5, 1, 2, 3, 4) + print('say_hello(name, times, 1, 2, 3, 4)') + say_hello('Zoe', 5, 1, 2, 3, 4) print() - print("say_hello(name, times, 1, 2, 3, 4, val=7, mode=prod)") - say_hello("Zoe", 5, 1, 2, 3, 4, val=7, mode="prod") + print('say_hello(name, times, 1, 2, 3, 4, val=7, mode=prod)') + say_hello('Zoe', 5, 1, 2, 3, 4, val=7, mode='prod') print() # THis isn't really an option @@ -30,7 +30,7 @@ def main(): def say_hello(name='friend', times=1, *args, **kwargs): - print(f"Hello {name} with times={times}, args={args}, kwargs={kwargs}!") + print(f'Hello {name} with times={times}, args={args}, kwargs={kwargs}!') if __name__ == '__main__': diff --git a/apps/py/ch03_lang/L06_ternary.py b/apps/py/ch03_lang/L06_ternary.py index 97176ae..3ee8d5b 100644 --- a/apps/py/ch03_lang/L06_ternary.py +++ b/apps/py/ch03_lang/L06_ternary.py @@ -1,10 +1,14 @@ def main(): while True: - text = input("Enter a number: ") + text = input('Enter a number: ') if not text: - print("Later...") + print('Later...') break num = int(text) - num_class = "small" if num < 100 else "huge!" - print(f"The number is {num_class}") + num_class = 'small' if num < 100 else 'huge!' + print(f'The number is {num_class}') + + +if __name__ == '__main__': + main() diff --git a/apps/py/ch03_lang/L08_closures.py b/apps/py/ch03_lang/L08_closures.py index eb24386..b8373ee 100644 --- a/apps/py/ch03_lang/L08_closures.py +++ b/apps/py/ch03_lang/L08_closures.py @@ -11,14 +11,14 @@ def main(): def create_counter(start_val, counter_id): - print(f"Creating a counter with start value {start_val}...") + print(f'Creating a counter with start value {start_val}...') inc = start_val def counter(): nonlocal inc inc += 1 - print(f"#{counter_id}: Counting {start_val}\t -->\t{inc}.") + print(f'#{counter_id}: Counting {start_val}\t -->\t{inc}.') return counter diff --git a/apps/py/ch03_lang/L09_types.py b/apps/py/ch03_lang/L09_types.py index 33a46ae..a7c2a27 100644 --- a/apps/py/ch03_lang/L09_types.py +++ b/apps/py/ch03_lang/L09_types.py @@ -2,13 +2,12 @@ class Wizard: - def __init__(self): self.name: Optional[str] = None self.level: int = 0 @staticmethod - def train(base_level: int) -> "Wizard": + def train(base_level: int) -> 'Wizard': w = Wizard() w.level = base_level @@ -18,7 +17,7 @@ def train(base_level: int) -> "Wizard": def main(): gandolf: Wizard = Wizard.train(100) gandolf.level += 1 - print(f"The wizard Gandolf is level {gandolf.level}") + print(f'The wizard Gandolf is level {gandolf.level}') if __name__ == '__main__': diff --git a/apps/py/ch03_lang/L10_errors.py b/apps/py/ch03_lang/L10_errors.py index 4be8460..f913e19 100644 --- a/apps/py/ch03_lang/L10_errors.py +++ b/apps/py/ch03_lang/L10_errors.py @@ -6,14 +6,14 @@ def main(): for v in values: try: - print(Fore.YELLOW + f"Calling sketchy_method with {v}...", flush=True) + print(Fore.YELLOW + f'Calling sketchy_method with {v}...', flush=True) sketchy_method(v) except BrokenPipeError: - print(Fore.LIGHTRED_EX + f" **** Network error, check our wifi.") + print(Fore.LIGHTRED_EX + ' **** Network error, check our wifi.') except ArithmeticError: - print(Fore.LIGHTRED_EX + f" **** Cannot compute with {v}!") + print(Fore.LIGHTRED_EX + f' **** Cannot compute with {v}!') except Exception as e: - print(Fore.LIGHTRED_EX + f" **** Error: {type(e).__name__} ==> {str(e)}") + print(Fore.LIGHTRED_EX + f' **** Error: {type(e).__name__} ==> {str(e)}') # finally: # print("Finally!") @@ -22,13 +22,13 @@ def sketchy_method(value: int): import random if not value: - raise ValueError(f"{value} is not valid.") + raise ValueError(f'{value} is not valid.') elif value % 6 == 0: raise ArithmeticError() elif random.randint(1, 10) == 3: - raise BrokenPipeError("Bad network!") + raise BrokenPipeError('Bad network!') - print("sketchy_method() actually worked!") + print('sketchy_method() actually worked!') if __name__ == '__main__': diff --git a/apps/py/ch03_lang/L11_using.py b/apps/py/ch03_lang/L11_using.py index 29bed3a..27d0be8 100644 --- a/apps/py/ch03_lang/L11_using.py +++ b/apps/py/ch03_lang/L11_using.py @@ -3,14 +3,14 @@ def main(): data = { - "name": "Michael", - "language": "Python", + 'name': 'Michael', + 'language': 'Python', } with open('file.json', 'w', encoding='utf-8') as fout: json.dump(data, fout, indent=True) - print("Saved to local file: file.json") + print('Saved to local file: file.json') if __name__ == '__main__': diff --git a/apps/py/ch03_lang/L12_switch.py b/apps/py/ch03_lang/L12_switch.py index dd8f65d..63df7e9 100644 --- a/apps/py/ch03_lang/L12_switch.py +++ b/apps/py/ch03_lang/L12_switch.py @@ -3,23 +3,22 @@ def main(): while True: - - text = input("Enter a number between 1 & 4: ") + text = input('Enter a number between 1 & 4: ') if not text: - print("Later!") + print('Later!') break num = int(text) with switch(num) as s: - s.case(1, lambda: print("One is fun!")) - s.case(2, lambda: print("2 * 2 = 4")) - s.case(3, lambda: print("Three and free.")) - s.case(4, lambda: print("4 more!")) + s.case(1, lambda: print('One is fun!')) + s.case(2, lambda: print('2 * 2 = 4')) + s.case(3, lambda: print('Three and free.')) + s.case(4, lambda: print('4 more!')) s.case(closed_range(10, 20), lambda: num * num) - s.default(lambda: print(f"Say what? {num}.")) + s.default(lambda: print(f'Say what? {num}.')) - print(f"Done and got {s.result}") + print(f'Done and got {s.result}') if __name__ == '__main__': diff --git a/apps/py/ch03_lang/switchlang.py b/apps/py/ch03_lang/switchlang.py index a41d52f..2cb59e8 100644 --- a/apps/py/ch03_lang/switchlang.py +++ b/apps/py/ch03_lang/switchlang.py @@ -4,10 +4,11 @@ class switch: """ - python-switch is a module-level implementation of the switch statement for Python. - See https://github.com/mikeckennedy/python-switch for full details. - Copyright Michael Kennedy (https://twitter.com/mkennedy) + python-switch is a module-level implementation of the switch statement for Python. + See https://github.com/mikeckennedy/python-switch for full details. + Copyright Michael Kennedy (https://twitter.com/mkennedy) """ + __no_result = uuid.uuid4() __default = uuid.uuid4() @@ -58,7 +59,7 @@ def case(self, key, func: Callable[[], Any], fallthrough=False): if isinstance(key, list): if not key: - raise ValueError("You cannot pass an empty collection as the case. It will never match.") + raise ValueError('You cannot pass an empty collection as the case. It will never match.') found = False for i in key: @@ -70,11 +71,11 @@ def case(self, key, func: Callable[[], Any], fallthrough=False): return found if key in self.cases: - raise ValueError(f"Duplicate case: {key}") + raise ValueError(f'Duplicate case: {key}') if not func: - raise ValueError("Action for case cannot be None.") + raise ValueError('Action for case cannot be None.') if not callable(func): - raise ValueError("Func must be callable.") + raise ValueError('Func must be callable.') self.cases.add(key) if key == self.value or not self._found and key == self.__default: @@ -92,8 +93,9 @@ def __exit__(self, exc_type, exc_val, exc_tb): raise exc_val if not self._func_stack: - raise Exception("Value does not match any case and there " - "is no default case: value {}".format(self.value)) + raise Exception( + 'Value does not match any case and there ' 'is no default case: value {}'.format(self.value) + ) for func in self._func_stack: # noinspection PyCallingNonCallable @@ -102,14 +104,13 @@ def __exit__(self, exc_type, exc_val, exc_tb): @property def result(self): if self.__result == switch.__no_result: - raise Exception("No result has been computed (did you access " - "switch.result inside the with block?)") + raise Exception('No result has been computed (did you access ' 'switch.result inside the with block?)') return self.__result def closed_range(start: int, stop: int, step=1) -> range: if start >= stop: - raise ValueError("Start must be less than stop.") + raise ValueError('Start must be less than stop.') return range(start, stop + step, step) diff --git a/apps/py/ch04_oop/models/basic_car.py b/apps/py/ch04_oop/models/basic_car.py index 2624873..0c0935e 100644 --- a/apps/py/ch04_oop/models/basic_car.py +++ b/apps/py/ch04_oop/models/basic_car.py @@ -3,4 +3,4 @@ class BasicCar(Car): def refuel(self): - print("BasicCar: Basic cars take any old gas.") + print('BasicCar: Basic cars take any old gas.') diff --git a/apps/py/ch04_oop/models/car.py b/apps/py/ch04_oop/models/car.py index defb973..71165fd 100644 --- a/apps/py/ch04_oop/models/car.py +++ b/apps/py/ch04_oop/models/car.py @@ -2,7 +2,6 @@ class Car(abc.ABC): - def __init__(self, model_name: str, engine_type: str, cylinders: int, base_price: float): self.base_price: float = base_price self.cylinders: int = cylinders @@ -10,7 +9,7 @@ def __init__(self, model_name: str, engine_type: str, cylinders: int, base_price self.model_name: str = model_name def drive(self): - print(f"Car: The {self.model_name} goes vroom!") + print(f'Car: The {self.model_name} goes vroom!') @abc.abstractmethod def refuel(self): @@ -21,7 +20,7 @@ def is_electric(self): return self.engine_type == 'electric' def __str__(self): - return f"{type(self).__name__}: Model: {self.model_name}, price: ${self.base_price:,.0f}" + return f'{type(self).__name__}: Model: {self.model_name}, price: ${self.base_price:,.0f}' def __repr__(self): return self.__str__() diff --git a/apps/py/ch04_oop/models/electric_car.py b/apps/py/ch04_oop/models/electric_car.py index 042dbf0..ff04246 100644 --- a/apps/py/ch04_oop/models/electric_car.py +++ b/apps/py/ch04_oop/models/electric_car.py @@ -2,14 +2,13 @@ class ElectricCar(Car): - def __init__(self, model_name: str, base_price: float): # first! super().__init__(model_name, 'electric', 0, base_price) # then other stuff.. def drive(self): - print(f"ElectricCar: The electric {self.model_name} zooms silently along!") + print(f'ElectricCar: The electric {self.model_name} zooms silently along!') def refuel(self): print(f'ElectricCar: The {self.model_name} is charging up.') diff --git a/apps/py/ch04_oop/models/parking_lot.py b/apps/py/ch04_oop/models/parking_lot.py index 6d7e69d..fbea0f7 100644 --- a/apps/py/ch04_oop/models/parking_lot.py +++ b/apps/py/ch04_oop/models/parking_lot.py @@ -4,19 +4,15 @@ class ParkingLot: - def __init__(self, spot_names: List[str]): # self.spots = dict() # for n in spot_names: # self.spots[n] = None - self.spots: Dict[str, Optional[Car]] = { - n: None - for n in spot_names - } + self.spots: Dict[str, Optional[Car]] = {n: None for n in spot_names} @staticmethod - def create(spots_per_level: int, levels: int) -> "ParkingLot": + def create(spots_per_level: int, levels: int) -> 'ParkingLot': names = [] level_names = ['A', 'B', 'C', 'D', 'E', 'G'] for ln in level_names[:levels]: diff --git a/apps/py/ch04_oop/models/sports_car.py b/apps/py/ch04_oop/models/sports_car.py index 668df56..fb4487a 100644 --- a/apps/py/ch04_oop/models/sports_car.py +++ b/apps/py/ch04_oop/models/sports_car.py @@ -2,9 +2,8 @@ class SportsCar(Car): - def drive(self): - print(f"SportsCar: The {self.model_name} tears along the highway!") + print(f'SportsCar: The {self.model_name} tears along the highway!') def refuel(self): - print(f"SportsCar: The {self.model_name} only wants the best gas.") + print(f'SportsCar: The {self.model_name} only wants the best gas.') diff --git a/apps/py/ch04_oop/program.py b/apps/py/ch04_oop/program.py index 5791bcc..e3fe45c 100644 --- a/apps/py/ch04_oop/program.py +++ b/apps/py/ch04_oop/program.py @@ -22,12 +22,12 @@ def park_cars(cars: List[Car]): for spot, car in lot: if car: - print(f"Spot: {spot} has car {car}.") + print(f'Spot: {spot} has car {car}.') def use_cars(cars): for car in cars: - print(f"{car.model_name} is electric? {car.is_electric}") + print(f'{car.model_name} is electric? {car.is_electric}') car.drive() car.refuel() print() diff --git a/apps/py/ch05_pypi/scrape.py b/apps/py/ch05_pypi/scrape.py index d03e08a..b040673 100644 --- a/apps/py/ch05_pypi/scrape.py +++ b/apps/py/ch05_pypi/scrape.py @@ -4,28 +4,28 @@ def main(): - print("Using Python packages") + print('Using Python packages') get_titles() def get_html(n: int) -> str: - print(Fore.YELLOW + f"Getting HTML for episode {n}...", flush=True) + print(Fore.YELLOW + f'Getting HTML for episode {n}...', flush=True) url = f'https://talkpython.fm/{n}' - resp = httpx.get(url) + resp = httpx.get(url, follow_redirects=True) resp.raise_for_status() return resp.text def get_title_from_html(n: int, html: str) -> str: - print(Fore.CYAN + f"Getting TITLE for episode {n}...", flush=True) + print(Fore.CYAN + f'Getting TITLE for episode {n}...', flush=True) soup = bs4.BeautifulSoup(html, 'html.parser') header = soup.select_one('h1') if not header: - return "MISSING" + return 'MISSING' return header.text.strip() diff --git a/apps/py/ch06_memory/doomed.py b/apps/py/ch06_memory/doomed.py index 72594d6..d405a75 100644 --- a/apps/py/ch06_memory/doomed.py +++ b/apps/py/ch06_memory/doomed.py @@ -1,10 +1,10 @@ class Doomed: def __init__(self, *friends): self.friends = list(friends) - print(f"Created Doomed at {id(self)}") + print(f'Created Doomed at {id(self)}') def __del__(self): - print(f"DEL Doomed at {id(self)}") + print(f'DEL Doomed at {id(self)}') def __str__(self): return self.__repr__() diff --git a/apps/py/ch06_memory/mem_explorer.py b/apps/py/ch06_memory/mem_explorer.py index 09cf184..11e720f 100644 --- a/apps/py/ch06_memory/mem_explorer.py +++ b/apps/py/ch06_memory/mem_explorer.py @@ -9,7 +9,7 @@ def main(): print() print() gcing() - print("PROGRAM ENDING!", flush=True) + print('PROGRAM ENDING!', flush=True) def ref_counting(): @@ -18,18 +18,18 @@ def ref_counting(): v1 = Doomed() v1_id = id(v1) - print(f"Step 1: Ref count is {memutil.refs(v1_id)}") + print(f'Step 1: Ref count is {memutil.refs(v1_id)}') v2 = v1 - print(f"Step 2: Ref count is {memutil.refs(v1_id)}") + print(f'Step 2: Ref count is {memutil.refs(v1_id)}') v2 = None - print(f"Step 3: Ref count is {memutil.refs(v1_id)}") + print(f'Step 3: Ref count is {memutil.refs(v1_id)}') v1 = None - print(f"Step 4: Ref count is {memutil.refs(v1_id)}") + print(f'Step 4: Ref count is {memutil.refs(v1_id)}') - print("End of method!", flush=True) + print('End of method!', flush=True) def gcing(): @@ -42,25 +42,17 @@ def gcing(): v1_id = id(v1) v2_id = id(v2) - print(f"Step 1: GC count is {memutil.refs(v1_id)} {memutil.refs(v2_id)}") + print(f'Step 1: GC count is {memutil.refs(v1_id)} {memutil.refs(v2_id)}') v1 = None v2 = None - print(f"Step 2: GC count is {memutil.refs(v1_id)} {memutil.refs(v2_id)}") + print(f'Step 2: GC count is {memutil.refs(v1_id)} {memutil.refs(v2_id)}') gc.collect() - print(f"Step 3: GC count is {memutil.refs(v1_id)} {memutil.refs(v2_id)}") + print(f'Step 3: GC count is {memutil.refs(v1_id)} {memutil.refs(v2_id)}') - print("End of method!", flush=True) + print('End of method!', flush=True) if __name__ == '__main__': main() - - - - - - - - diff --git a/apps/py/ch06_memory/memutil.py b/apps/py/ch06_memory/memutil.py index 942216e..45be8d0 100644 --- a/apps/py/ch06_memory/memutil.py +++ b/apps/py/ch06_memory/memutil.py @@ -3,7 +3,7 @@ # We use ctypes module to access our unreachable objects by memory address. class PyObject(ctypes.Structure): - _fields_ = [("refcnt", ctypes.c_long)] + _fields_ = [('refcnt', ctypes.c_long)] def refs(obj_id) -> int: diff --git a/apps/py/ch07_web/guitary/data/guitar.py b/apps/py/ch07_web/guitary/data/guitar.py index f0800d0..760e9b8 100644 --- a/apps/py/ch07_web/guitary/data/guitar.py +++ b/apps/py/ch07_web/guitary/data/guitar.py @@ -1,5 +1,4 @@ class Guitar: - def __init__(self, name: str, price: float, img: str, style: str): self.style = style self.img = img diff --git a/apps/py/ch07_web/guitary/services/catalog_service.py b/apps/py/ch07_web/guitary/services/catalog_service.py index 0cf2f41..61dd049 100644 --- a/apps/py/ch07_web/guitary/services/catalog_service.py +++ b/apps/py/ch07_web/guitary/services/catalog_service.py @@ -20,10 +20,6 @@ def all_guitars(style: Optional[str]) -> List[Guitar]: if style is None or style == 'all': return guitars - filtered_guitars = [ - g - for g in guitars - if g.style == style - ] + filtered_guitars = [g for g in guitars if g.style == style] return filtered_guitars diff --git a/apps/py/ch07_web/speed_test.py b/apps/py/ch07_web/speed_test.py index b344f02..9240bae 100644 --- a/apps/py/ch07_web/speed_test.py +++ b/apps/py/ch07_web/speed_test.py @@ -9,9 +9,9 @@ def main(): url = input("What's the URL? ").strip() - times = int(input("How many rounds ? ")) + times = int(input('How many rounds ? ')) - print(f"Running timing against {url}, {times} times.") + print(f'Running timing against {url}, {times} times.') total_time = 0.0 with no_ssl_verification(): with requests.Session() as session: diff --git a/apps/py/ch07_web/wsgi.py b/apps/py/ch07_web/wsgi.py index 4fe26e7..68578b6 100644 --- a/apps/py/ch07_web/wsgi.py +++ b/apps/py/ch07_web/wsgi.py @@ -1,2 +1 @@ from guitary.app import app - diff --git a/apps/py/ch08_db/guitary/data/data_loader.py b/apps/py/ch08_db/guitary/data/data_loader.py index c4cec28..dbe810d 100644 --- a/apps/py/ch08_db/guitary/data/data_loader.py +++ b/apps/py/ch08_db/guitary/data/data_loader.py @@ -3,26 +3,31 @@ def load_guitars_if_empty(): - with session_factory.create_session() as ctx: count = ctx.session.query(Guitar).count() if count > 0: - print(f"Not adding new data, there are {count} guitars already.") + print(f'Not adding new data, there are {count} guitars already.') return guitars = [ Guitar(name='AX Black', price=499, img='/static/img/guitars/ax-black.jpg', style='electric'), - Guitar(name='Jet Black Electric', price=599, img='/static/img/guitars/jet-black-electric.jpg', - style='electric'), + Guitar( + name='Jet Black Electric', price=599, img='/static/img/guitars/jet-black-electric.jpg', style='electric' + ), Guitar(name='Weezer Classic', price=1499, img='/static/img/guitars/weezer-classic.jpg', style='electric'), Guitar(name='Acoustic Black', price=1298, img='/static/img/guitars/black-acoustic.jpg', style='acoustic'), Guitar(name='Mellow Yellow', price=799, img='/static/img/guitars/mellow-yellow.jpg', style='electric'), Guitar(name='White Vibes', price=699, img='/static/img/guitars/white-vibes.jpg', style='electric'), - Guitar(name='Brush Riffs', price=599, img='/static/img/guitars/brushed-black-electric.jpg', - style='electric'), + Guitar( + name='Brush Riffs', price=599, img='/static/img/guitars/brushed-black-electric.jpg', style='electric' + ), Guitar(name="Nature's Song", price=799, img='/static/img/guitars/natures-song.jpg', style='electric'), - Guitar(name='Electric Wood Grain', price=399, img='/static/img/guitars/woodgrain-electric.jpg', - style='electric'), + Guitar( + name='Electric Wood Grain', + price=399, + img='/static/img/guitars/woodgrain-electric.jpg', + style='electric', + ), ] # Do work here... diff --git a/apps/py/ch08_db/guitary/data/session_factory.py b/apps/py/ch08_db/guitary/data/session_factory.py index 3f449f3..8145f5b 100644 --- a/apps/py/ch08_db/guitary/data/session_factory.py +++ b/apps/py/ch08_db/guitary/data/session_factory.py @@ -24,7 +24,7 @@ def global_init(db_name: str): def create_tables(): if not __engine: - raise Exception("You must call global_init() first.") + raise Exception('You must call global_init() first.') # noinspection PyUnresolvedReferences from guitary.data.guitar import Guitar @@ -35,7 +35,7 @@ def create_tables(): def create_session() -> ContextSession: if not __factory: - raise Exception("You must call global_init() first.") + raise Exception('You must call global_init() first.') session = __factory() session.expire_on_commit = False diff --git a/apps/py/ch08_db/guitary/services/catalog_service.py b/apps/py/ch08_db/guitary/services/catalog_service.py index 6e4c997..115a5dd 100644 --- a/apps/py/ch08_db/guitary/services/catalog_service.py +++ b/apps/py/ch08_db/guitary/services/catalog_service.py @@ -12,8 +12,6 @@ def all_guitars(style: Optional[str]) -> List[Guitar]: return list(guitars) # noinspection PyUnresolvedReferences - filtered_guitars = list(ctx.session.query(Guitar) - .filter(Guitar.style == style) - .order_by(Guitar.price.desc())) + filtered_guitars = list(ctx.session.query(Guitar).filter(Guitar.style == style).order_by(Guitar.price.desc())) return filtered_guitars diff --git a/apps/py/ch09_testing/guitar_app.py b/apps/py/ch09_testing/guitar_app.py index 484fd4f..3e8cba0 100644 --- a/apps/py/ch09_testing/guitar_app.py +++ b/apps/py/ch09_testing/guitar_app.py @@ -2,16 +2,16 @@ def main(): - print("Welcome to the guitar app.") + print('Welcome to the guitar app.') while True: print() - style = input("What style of guitar do you want to see? ") + style = input('What style of guitar do you want to see? ') if not style: - print("cya") + print('cya') break guitars = lib.all_guitars(style) - print(f"We found {len(guitars)} guitars.") + print(f'We found {len(guitars)} guitars.') for idx, g in enumerate(guitars, start=1): print(f'{idx}. {g.name} for ${g.price:,.0f} ({g.style})') diff --git a/apps/py/ch09_testing/lib.py b/apps/py/ch09_testing/lib.py index 6b72431..acfbed9 100644 --- a/apps/py/ch09_testing/lib.py +++ b/apps/py/ch09_testing/lib.py @@ -3,8 +3,7 @@ class Guitar: - def __init__(self, name: str = None, price: float = 0.0, - img: str = None, style: str = None): + def __init__(self, name: str = None, price: float = 0.0, img: str = None, style: str = None): self.style = style self.img = img self.price = price @@ -17,27 +16,23 @@ def all_guitars(style: Optional[str]) -> List[Guitar]: style = style.lower() - log(f"Guitars for {style}") + log(f'Guitars for {style}') guitars = get_guitars_from_db() if style == 'all': return guitars - return [ - g - for g in guitars - if g.style == style - ] + return [g for g in guitars if g.style == style] def log(msg: str): # raise Exception("NO LOG!") - print(Fore.YELLOW + "LOGGING THIS TO A FILE, SHOULD NOT SEE IN TEST: " + Fore.WHITE + f"{msg}.") + print(Fore.YELLOW + 'LOGGING THIS TO A FILE, SHOULD NOT SEE IN TEST: ' + Fore.WHITE + f'{msg}.') # noinspection DuplicatedCode def get_guitars_from_db(): # raise Exception("NO DB!") - print(Fore.YELLOW + "GETTING GUITARS FROM DB! Should not see in test." + Fore.WHITE) + print(Fore.YELLOW + 'GETTING GUITARS FROM DB! Should not see in test.' + Fore.WHITE) # This guitar data simulates what we would actually get back from the database. guitars = [ Guitar('AX Black', 499, '/static/img/guitars/ax-black.jpg', style='electric'), diff --git a/apps/py/ch09_testing/test_lib.py b/apps/py/ch09_testing/test_lib.py index b9026bd..7893cec 100644 --- a/apps/py/ch09_testing/test_lib.py +++ b/apps/py/ch09_testing/test_lib.py @@ -4,6 +4,7 @@ import lib import pytest_mock + # noinspection PyUnresolvedReferences from test_fixtures import guitar_data diff --git a/apps/py/ch10_async/ascrape.py b/apps/py/ch10_async/ascrape.py index e8949f9..f34fd76 100644 --- a/apps/py/ch10_async/ascrape.py +++ b/apps/py/ch10_async/ascrape.py @@ -6,22 +6,22 @@ def main(): - print("Python async web scraper") + print('Python async web scraper') t0 = datetime.datetime.now() get_titles().result() dt = datetime.datetime.now() - t0 - print(f"Finished in {dt.total_seconds():,.2f} seconds.") + print(f'Finished in {dt.total_seconds():,.2f} seconds.') @unsync async def get_html(n: int) -> str: - print(Fore.YELLOW + f"Getting HTML for episode {n}...", flush=True) + print(Fore.YELLOW + f'Getting HTML for episode {n}...', flush=True) url = f'https://talkpython.fm/{n}' # The "async with" syntax ensures that all active connections are closed on exit. async with httpx.AsyncClient() as client: - resp = await client.get(url) + resp = await client.get(url, follow_redirects=True) resp.raise_for_status() return resp.text @@ -30,12 +30,12 @@ async def get_html(n: int) -> str: # @unsync # <-- will run get_title_from_html() on a background thread. # @unsync(cpu_bound=True) # <-- will run get_title_from_html() on a subprocess. def get_title_from_html(n: int, html: str) -> str: - print(Fore.CYAN + f"Getting TITLE for episode {n}...", flush=True) + print(Fore.CYAN + f'Getting TITLE for episode {n}...', flush=True) soup = bs4.BeautifulSoup(html, 'html.parser') header = soup.select_one('h1') if not header: - return "MISSING" + return 'MISSING' return header.text.strip() @@ -46,6 +46,7 @@ def get_title_from_html(n: int, html: str) -> str: # title = get_title_from_html(n, html) # print(Fore.GREEN + title) + @unsync async def get_titles(): tasks = [] diff --git a/apps/py/ch11_notebooks/lorenz.py b/apps/py/ch11_notebooks/lorenz.py index c83aab2..95c02b4 100644 --- a/apps/py/ch11_notebooks/lorenz.py +++ b/apps/py/ch11_notebooks/lorenz.py @@ -3,7 +3,8 @@ import numpy as np from scipy import integrate -def solve_lorenz(sigma=10.0, beta=8./3, rho=28.0): + +def solve_lorenz(sigma=10.0, beta=8.0 / 3, rho=28.0): """Plot a solution to the Lorenz differential equations.""" max_time = 4.0 @@ -17,7 +18,7 @@ def solve_lorenz(sigma=10.0, beta=8./3, rho=28.0): ax.set_xlim((-25, 25)) ax.set_ylim((-35, 35)) ax.set_zlim((5, 55)) - + def lorenz_deriv(x_y_z, t0, sigma=sigma, beta=beta, rho=rho): """Compute the time-derivative of a Lorenz system.""" x, y, z = x_y_z @@ -28,19 +29,18 @@ def lorenz_deriv(x_y_z, t0, sigma=sigma, beta=beta, rho=rho): x0 = -15 + 30 * np.random.random((N, 3)) # Solve for the trajectories - t = np.linspace(0, max_time, int(250*max_time)) - x_t = np.asarray([integrate.odeint(lorenz_deriv, x0i, t) - for x0i in x0]) - + t = np.linspace(0, max_time, int(250 * max_time)) + x_t = np.asarray([integrate.odeint(lorenz_deriv, x0i, t) for x0i in x0]) + # choose a different color for each trajectory colors = plt.cm.viridis(np.linspace(0, 1, N)) for i in range(N): - x, y, z = x_t[i,:,:].T + x, y, z = x_t[i, :, :].T lines = ax.plot(x, y, z, '-', c=colors[i]) plt.setp(lines, linewidth=2) angle = 104 ax.view_init(30, angle) plt.show() - return t, x_t \ No newline at end of file + return t, x_t diff --git a/apps/py/ch12_deployment/server_setup/server_setup.sh b/apps/py/ch12_deployment/server_setup/server_setup.sh index da5682e..ceeeb0c 100644 --- a/apps/py/ch12_deployment/server_setup/server_setup.sh +++ b/apps/py/ch12_deployment/server_setup/server_setup.sh @@ -15,8 +15,9 @@ sudo apt-get install -y -q nginx # for gzip support in uwsgi sudo apt-get install --no-install-recommends -y -q libpcre3-dev libz-dev -# Stop the hackers -sudo apt install fail2ban -y +# Fail2ban no longer supported +# Skip this for now. +# sudo apt install fail2ban -y ufw allow 22 ufw allow 80 @@ -78,9 +79,27 @@ update-rc.d nginx enable service nginx restart -# Optionally add SSL support via Let's Encrypt: -# https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-18-04 +# Optionally add SSL support via Let's Encrypt +# NOTE: These steps have changed since the recording. -add-apt-repository ppa:certbot/certbot -apt install python-certbot-nginx +####### NEW STEPS ############################################### +# See https://certbot.eff.org/instructions?ws=nginx&os=ubuntufocal&tab=standard + +# Because always a good idea :) +apt update +apt upgrade + +# Not need even though it's in the instructions, is installed on Ubuntu +# Skip -> install snapd https://snapcraft.io/docs/installing-snapd + +snap install --classic certbot +ln -s /snap/bin/certbot /usr/bin/certbot certbot --nginx -d guitary.talkpython.com + +####### THESE ARE THE OLD STEPS ################################# +# +## https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-18-04 +# +#add-apt-repository ppa:certbot/certbot +#apt install python-certbot-nginx +#certbot --nginx -d guitary.talkpython.com diff --git a/apps/py/requirements.piptools b/apps/py/requirements.piptools new file mode 100644 index 0000000..a310a83 --- /dev/null +++ b/apps/py/requirements.piptools @@ -0,0 +1,14 @@ +# comments +colorama +bs4 +httpx +flask +matplotlib +numpy +scipy +sqlalchemy +requests +pytest +pytest-mock +pytest-clarity +unsync diff --git a/apps/py/requirements.txt b/apps/py/requirements.txt index 31d73e8..0e6648b 100644 --- a/apps/py/requirements.txt +++ b/apps/py/requirements.txt @@ -1,11 +1,116 @@ -# comments -colorama -bs4 -httpx -flask -sqlalchemy -requests -pytest -pytest-mock -pytest-clarity -unsync +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.piptools --output-file requirements.txt +anyio==4.10.0 + # via httpx +beautifulsoup4==4.13.4 + # via bs4 +blinker==1.9.0 + # via flask +bs4==0.0.2 + # via -r requirements.piptools +certifi==2025.8.3 + # via + # httpcore + # httpx + # requests +charset-normalizer==3.4.3 + # via requests +click==8.2.1 + # via flask +colorama==0.4.6 + # via -r requirements.piptools +contourpy==1.3.3 + # via matplotlib +cycler==0.12.1 + # via matplotlib +flask==3.1.2 + # via -r requirements.piptools +fonttools==4.59.1 + # via matplotlib +h11==0.16.0 + # via httpcore +httpcore==1.0.9 + # via httpx +httpx==0.28.1 + # via -r requirements.piptools +idna==3.10 + # via + # anyio + # httpx + # requests +iniconfig==2.1.0 + # via pytest +itsdangerous==2.2.0 + # via flask +jinja2==3.1.6 + # via flask +kiwisolver==1.4.9 + # via matplotlib +markdown-it-py==4.0.0 + # via rich +markupsafe==3.0.2 + # via + # flask + # jinja2 + # werkzeug +matplotlib==3.10.5 + # via -r requirements.piptools +mdurl==0.1.2 + # via markdown-it-py +numpy==2.3.2 + # via + # -r requirements.piptools + # contourpy + # matplotlib + # scipy +packaging==25.0 + # via + # matplotlib + # pytest +pillow==11.3.0 + # via matplotlib +pluggy==1.6.0 + # via pytest +pprintpp==0.4.0 + # via pytest-clarity +pygments==2.19.2 + # via + # pytest + # rich +pyparsing==3.2.3 + # via matplotlib +pytest==8.4.1 + # via + # -r requirements.piptools + # pytest-clarity + # pytest-mock +pytest-clarity==1.0.1 + # via -r requirements.piptools +pytest-mock==3.14.1 + # via -r requirements.piptools +python-dateutil==2.9.0.post0 + # via matplotlib +requests==2.32.5 + # via -r requirements.piptools +rich==14.1.0 + # via pytest-clarity +scipy==1.16.1 + # via -r requirements.piptools +six==1.17.0 + # via python-dateutil +sniffio==1.3.1 + # via anyio +soupsieve==2.7 + # via beautifulsoup4 +sqlalchemy==2.0.43 + # via -r requirements.piptools +typing-extensions==4.14.1 + # via + # beautifulsoup4 + # sqlalchemy +unsync==1.4.0 + # via -r requirements.piptools +urllib3==2.5.0 + # via requests +werkzeug==3.1.3 + # via flask diff --git a/readme_resources/python-for-dotnet-course.png b/readme_resources/python-for-dotnet-course.png index 0eaf0f8..7aaf816 100644 Binary files a/readme_resources/python-for-dotnet-course.png and b/readme_resources/python-for-dotnet-course.png differ diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..5537773 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,42 @@ +# [ruff] +line-length = 120 +format.quote-style = "single" + +# Enable Pyflakes `E` and `F` codes by default. +select = ["E", "F"] +ignore = [] + +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".ruff_cache", + ".svn", + ".tox", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + ".env", + ".venv", + "venv", +] +per-file-ignores = {} + +# Allow unused variables when underscore-prefixed. +# dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +# Assume Python 3.11. +target-version = "py311" + +#[tool.ruff.mccabe] +## Unlike Flake8, default to a complexity level of 10. +mccabe.max-complexity = 10