Impractical Python Jeremy Reichman

@jaharmi

Tamman Technologies, Inc.

In [160]: %%html

PSU Mac Admins Conference 2019 Python survey

This survey will ask you about Python, general scripting and programming, Apple platform administration, and stuff of that sort. It will take a minimum of 3 years to complete.

I am attending the Penn State Mac Admins Conference 2019.

Yes

No

Besides attending the conference, I am also a speaker.

Yes

No

Not at the conference

During the Krst three days of the conference, I have already Please take this survey to temporarily deflect your attention from any and all deficiencies of this session

https://forms.gle/YhWXcZBsrb3LJTfr9 (https://forms.gle/YhWXcZBsrb3LJTfr9) THAT SUCKS! THAT SUCKS BIG TIME! Not a typical presentation Jupyter Notebook It's different!

It looks just like this! Credit: macOS Mojave. Why you might want Python

Data science!

Opportunities!

Keep your Mac instead of replacing it with an iPad Pro! Modern Python vs. Legacy Python JetBrains Python Dev Ecosystem 2019 (https://www.jetbrains.com/lp/devecosystem- 2019/python/) In [9]: import matplotlib.pyplot as plt

In [30]: labels = ["Python 3", "Python 2"] sizes = [87, 13] explode = (0, 0.25) In [31]: plt.pie( sizes, labels=labels, startangle=90, autopct='%1.0f%%', explode=explode, ) plt.show() End of Python 2

No more updates as of January 1, 2020 Not even for security! It's dead!

Credit: Marvel Studios, Avengers Endgame poster, modified. Why you might not want Python

Avoiding Python 2 See "Scripting Language Runtimes" in macOS Catalina Beta Release Notes (https://developer.apple.com/documentation/macos_release_notes/macos_catalina_10_15_beta_3_release_notes)

Having to install and manage Python Would one or more Mac admin-specific "distributions" of Modern Python be worthwhile?

Swift, SwiftUI, and their place in Apple platforms Python 3 Redesigned Dark mode

Credit: Python Software Foundation, Python logo. Also, this is NOT REAL. Getting started with Python 3

Python.org (https://python.org/) Relocatable Python (https://github.com/gregneagle/relocatable-python) "Relocatable Python framework containing PyObjC" "Ideal for embedding into an application's Frameworks directory" Anaconda (https://anaconda.org) Python plus many modules, built and tested to work together Makes Jupyter Notebook easier to use Jupyter Notebook, again Gave up all modern presentation conveniences

Drag and drop

Easy editing

Knowing what I was doing

Animations, builds, MAGIC MOVE Not sure if this is 20 minutes into the future or what

Credit: ABC network Resign myself to troubleshooting my presentation environment In [4]: %%html

Assumption that every "slide" is for code

Which executes somewhere, wherever the "kernel" is running Credit: Jupyter Documentation.

And provides for some interesting interactivity In [16]: !echo $PATH

/home/nbuser/anaconda3_501/bin:/usr/local/bin:/usr/local/sbin:/usr/local/bin :/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/home/nbuser/.local/bin In [75]: !conda env list

# conda environments: # base /Users/jeremy/anaconda3 anaconda3_501 * /Users/jeremy/anaconda3/envs/anaconda3_501 In [17]: !type pip

pip is /home/nbuser/anaconda3_501/bin/pip

In [21]: !conda list 2>/dev/null

# packages in environment at /Users/jeremy/anaconda3/envs/anaconda3_501: # # Name Version Build Channel appdirs 1.4.3 py_1 conda-forge appnope 0.1.0 py37_1000 conda-forge arrow 0.14.2 py37_0 conda-forge asn1crypto 0.24.0 py37_1003 conda-forge attrs 19.1.0 py_0 conda-forge backcall 0.1.0 py_0 conda-forge backports 1.0 py_2 conda-forge binaryornot 0.4.4 py_1 conda-forge black 19.3b0 py_0 conda-forge blackcellmagic 0.0.2 pypi_0 pypi bleach 3.1.0 py_0 conda-forge bzip2 1.0.6 h1de35cc_1002 conda-forge ca-certificates 2019.5.15 0 cachetools 2.1.0 py_0 conda-forge certifi 2019.6.16 py37_0 cffi 1.12.3 py37hccf1714_0 conda-forge chardet 3.0.4 py37_1003 conda-forge click 7.0 py_0 conda-forge configparser 3.7.3 py37_1 conda-forge cookiecutter 1.6.0 py37_1000 conda-forge cryptography 2.7 py37h212c5bf_0 conda-forge csvfaker 1.0.5 pypi_0 pypi cycler 0.10.0 py37_0 dbus 1.13.6 h2f22bb5_0 conda-forge decorator 4.4.0 py_0 conda-forge defusedxml 0.5.0 py_1 conda-forge df2gspread 1.0.4 py37_1000 conda-forge editorconfig 0.12.2 pypi_0 pypi entrypoints 0.3 py37_1000 conda-forge et_xmlfile 1.0.1 py_1001 conda-forge expat 2.2.5 h6de7cb9_1003 conda-forge faker 1.0.7 py37_0 conda-forge freetype 2.9.1 hb4e5f40_0 future 0.17.1 py37_1000 conda-forge gettext 0.19.8.1 h46ab8bc_1002 conda-forge glib 2.58.3 h9d45998_1001 conda-forge google--python-client 1.7.9 py_0 conda-forge google-auth 1.6.3 py_0 conda-forge google-auth-httplib2 0.0.3 py_2 conda-forge gspread 3.1.0 py_0 conda-forge httplib2 0.13.0 py37_0 conda-forge icu 58.2 h0a44026_1000 conda-forge idna 2.8 py37_1000 conda-forge ipykernel 5.1.1 py37h24bf2e0_0 conda-forge ipython 7.6.1 py37h5ca1d4c_0 conda-forge ipython_genutils 0.2.0 py_1 conda-forge ipywidgets 7.5.0 py_0 conda-forge jdcal 1.4.1 py_0 conda-forge jedi 0.14.0 py37_0 conda-forge jinja2 2.10.1 py_0 conda-forge jinja2-time 0.2.0 py_2 conda-forge jpeg 9c h1de35cc_1001 conda-forge jsonschema 3.0.1 py37_0 conda-forge jupyter 1.0.0 py37_7 jupyter_client 5.2.4 py_3 conda-forge jupyter_console 6.0.0 py_0 conda-forge jupyter_contrib_core 0.3.3 py_2 conda-forge jupyter_contrib_nbextensions 0.5.1 py37_0 conda-forge jupyter_core 4.4.0 py_0 conda-forge jupyter_highlight_selected_word 0.2.0 py37_1000 conda-for ge jupyter_latex_envs 1.4.4 py37_1000 conda-forge jupyter_nbextensions_configurator 0.4.1 py37_0 conda-f orge kiwisolver 1.1.0 py37h0a44026_0 libblas 3.8.0 10_openblas conda-forge libcblas 3.8.0 10_openblas conda-forge libcxx 8.0.0 4 conda-forge libcxxabi 8.0.0 4 conda-forge 3.2.1 h6de7cb9_1006 conda-forge libgfortran 3.0.1 0 conda-forge libiconv 1.15 h01d97ff_1005 conda-forge liblapack 3.8.0 10_openblas conda-forge libopenblas 0.3.6 hd44dcd8_4 conda-forge libpng 1.6.37 h2573ce8_0 conda-forge libsodium 1.0.16 h1de35cc_1001 conda-forge libxml2 2.9.9 hd80cff7_1 conda-forge libxslt 1.1.32 h1e79283_1003 conda-forge lxml 4.3.4 py37h08abf6f_0 conda-forge markupsafe 1.1.1 py37h1de35cc_0 conda-forge matplotlib 3.1.0 py37h54f8f79_0 mistune 0.8.4 py37h1de35cc_1000 conda-forge nb-conda 2.2.1 pypi_0 pypi nb_conda 2.2.1 py37_2 conda-forge nb_conda_kernels 2.2.2 py37_0 nbconvert 5.5.0 py_0 conda-forge nbformat 4.4.0 py_1 conda-forge ncurses 6.1 h0a44026_1002 conda-forge notebook 5.7.8 py37_0 numpy 1.16.4 py37h6b0580a_0 conda-forge oauth2client 4.1.3 py_0 conda-forge openblas 0.3.6 hd44dcd8_4 conda-forge openpyxl 2.6.2 py_0 conda-forge openssl 1.1.1c h1de35cc_1 pandas 0.24.2 py37h86efe34_0 conda-forge pandoc 2.7.3 0 conda-forge pandocfilters 1.4.2 py_1 conda-forge parso 0.5.0 py_0 conda-forge pcre 8.41 h0a44026_1003 conda-forge pexpect 4.7.0 py37_0 conda-forge pickleshare 0.7.5 py37_1000 conda-forge pip 19.1.1 py37_0 conda-forge poyo 0.4.2 py_0 conda-forge prometheus_client 0.7.1 py_0 conda-forge prompt_toolkit 2.0.9 py_0 conda-forge ptyprocess 0.6.0 py_1001 conda-forge pyasn1 0.4.5 py_0 conda-forge pyasn1-modules 0.2.5 py_0 conda-forge pycparser 2.19 py37_1 conda-forge pycrypto 2.6.1 py37h1de35cc_1002 conda-forge pygments 2.4.2 py_0 conda-forge pyjokes 0.5.0 py_0 conda-forge pyopenssl 19.0.0 py37_0 conda-forge pyparsing 2.4.0 py_0 pyqt 5.9.2 py37h2a560b1_0 conda-forge pyrsistent 0.15.3 py37h01d97ff_0 conda-forge pysocks 1.7.0 py37_0 conda-forge python 3.7.3 h93065d6_1 conda-forge python-dateutil 2.8.0 py_0 conda-forge python-magic 0.4.15 pypi_0 pypi pytz 2019.1 py_0 conda-forge pyyaml 5.1.1 py37h01d97ff_0 conda-forge pyzmq 18.0.2 py37h2d07e9b_0 conda-forge qt 5.9.7 h93ee506_2 conda-forge qtconsole 4.5.1 py_0 conda-forge readline 8.0 hcfe32e1_0 conda-forge requests 2.22.0 py37_0 conda-forge rise 5.5.1 py37_0 conda-forge rsa 3.4.2 py_1 conda-forge send2trash 1.5.0 py_0 conda-forge setuptools 41.0.1 py37_0 conda-forge simplejson 3.16.0 py37h1de35cc_1002 conda-forge sip 4.19.8 py37h0a44026_1000 conda-forge six 1.12.0 py37_1000 conda-forge sqlite 3.28.0 hb7d70f7_1 conda-forge tabulate 0.8.3 py_0 conda-forge terminado 0.8.2 py37_0 conda-forge testpath 0.4.2 py_1001 conda-forge text-unidecode 1.2 py_0 conda-forge tk 8.6.9 h2573ce8_1002 conda-forge toml 0.10.0 py_0 conda-forge tornado 6.0.3 py37h01d97ff_0 conda-forge traitlets 4.3.2 py37_1000 conda-forge uritemplate 3.0.0 py_1 conda-forge urllib3 1.24.3 py37_0 conda-forge wcwidth 0.1.7 py_1 conda-forge webencodings 0.5.1 py_1 conda-forge wheel 0.33.4 py37_0 conda-forge whichcraft 0.5.2 py_1 conda-forge widgetsnbextension 3.5.0 py37_0 conda-forge xz 5.2.4 h1de35cc_1001 conda-forge yaml 0.1.7 h1de35cc_1001 conda-forge youtube-dl 2019.7.2 pypi_0 pypi zeromq 4.3.1 h0a44026_1000 conda-forge zlib 1.2.11 h1de35cc_1004 conda-forge In [40]: def double(x): print(x * 2)

double(42)

84 In [35]: from ipywidgets import interact

def my_function(x): return x

# create a slider interact(my_function, x=20)

Out[35]: In [42]: import pyjokes In [43]: pyjokes.get_joke()

Out[43]: "How do you know whether a person is a Vim user? Don't worry, they'll tell y ou." And now how 1. Install Anaconda with Python 3

1. Launch Jupyter Notebook and create notebook project 1. Try to host my project online

1. Create an Anaconda environment to install modules 1. Stuff happens

1. Get really frustrated and delete everything 1. Install Anaconda with Python 3 again

1. Copy my Jupyter Notebook project with environment.yml 1. Create local environment, run it, and launch Jupyter again

conda env list conda activate anaconda3_501 #Wrong! Need shell to be set up conda init zsh source ~/.zshrc conda activate anaconda3_501 ; jupyter_mac.command

1. Things worked a bit better Code and tools Microsoft Visual Studio Code

Python (https://code.visualstudio.com/docs/languages/python) is #1 plugin Autocomplete and IntelliSense for code completion for Python Uses machine learning Use elsewhere with the standalone Python language server (https://blogs.msdn.microsoft.com/pythonengineering/2018/07/18/introducing- the-python-language-server/) with the Language Server Protocol Linting Testing and debugging Cookiecutter (https://github.com/cookiecutter/cookiecutter)

Command line tool Create projects from templates In [ ]: !cookiecutter https://github.com/audreyr/cookiecutter-pypackage.git

You've downloaded /Users/jeremy/.cookiecutters/cookiecutter-pypackage before . Is it okay to delete and re-download it? [yes]: Set up for Git

Pre-commit hooks

For Mac Admins (https://github.com/homebysix/pre-commit-macadmin) Large selection (https://pre-commit.com/hooks.html) Black (https://github.com/python/black) "is the uncompromising Python code formatter"

code looks the same regardless of the project you're reading makes code review faster by producing the smallest diffs possible Mypy (http://www.mypy-lang.org) is "an optional static type checker for Python that aims to combine the benefits of dynamic (or "duck") typing and static typing" Code style in .editorconfig file

Indentation style - tabs vs. spaces Indentation size Line endings Etc. Configuration file # Top most EditorConfig file root = true

# Unix-style newlines with a newline ending every file [*] indent_style = space indent_size = 4 end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true In [ ]: import configparser import os from pathlib import Path

In [ ]: editorconfig = configparser.ConfigParser() In [48]: editorconfig_file = "resources/base_editorconfig" editorconfig.read(editorconfig_file) editorconfig.sections()

Out[48]: ['*'] In [51]: editorconfig["*.{js,py,plist,mobileconfig,recipe}"] = { "charset": "utf-8" }

In [52]: new_editorconfig_file = "resources/new_editorconfig"

if Path(new_editorconfig_file).exists: os.remove(new_editorconfig_file) with open (new_editorconfig_file, 'w') as new_file_write: editorconfig.write(new_file_write) In [53]: print(Path(new_editorconfig_file).read_text())

[*] indent_style = space indent_size = 4 end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true

[*.{js,py,plist,mobileconfig,recipe}] charset = utf-8 In [153]: editorconfig["*.py"] = { "mode": "python" }

In [154]: with open(new_editorconfig_file, 'w') as new_file_write: editorconfig.write(new_file_write) In [156]: print(Path(new_editorconfig_file).read_text())

[*] indent_style = space indent_size = 4 end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true

[*.{js,py,plist,mobileconfig,recipe}] charset = utf-8

[*.py] mode = python Files In [17]: import magic $ sudo /opt/local/bin/port selfupdate $ sudo /opt/local/bin/port install file Warning: xcodebuild exists but failed to execute Warning: does not appear to be installed; most ports will likely fail to build. ---> Computing dependencies for file The following dependencies will be installed: libmagic Continue? [Y/n]: ---> Fetching archive for libmagic ---> Attempting to fetch libmagic-5.37_0.darwin_18.x86_64.tbz2 from https://pa ckages.macports.org/libmagic ---> Attempting to fetch libmagic-5.37_0.darwin_18.x86_64.tbz2.rmd160 from htt ps://packages.macports.org/libmagic ---> Installing libmagic @5.37_0 ---> Activating libmagic @5.37_0 ---> Cleaning libmagic ---> Fetching archive for file ---> Attempting to fetch file-5.37_0.darwin_18.x86_64.tbz2 from https://packag es.macports.org/file ---> Attempting to fetch file-5.37_0.darwin_18.x86_64.tbz2.rmd160 from https:/ /packages.macports.org/file ---> Installing file @5.37_0 ---> Activating file @5.37_0 ---> Cleaning file ---> Scanning binaries for linking errors ---> No broken files found. ---> No broken ports found. In [11]: # Zip up a dictionary of files # Feed to magic magic.from_file("aznbsetup.sh")

Out[11]: 'POSIX shell script, ASCII text executable'

In [20]: from pathlib import Path In [56]: magic_test_files = [ "/Applications/Safari.app/Contents/Resources/[email protected]", "/Applications/Keynote.app/Contents/Resources/Acknowledgments.pdf", "/Applications/iTunes.app/Contents/Resources/iTunes.icns", "/Applications/Utilities/Terminal.app/Contents/Resources/Fonts/SFMono-Bold.o tf", "~/Library/Application Support/Google/Chrome/Local State", "~/Library/Application Support/Google/Chrome/Default/Extensions/aapocclcgogk mnckokdopfmhonfmgoek/0.10_0/_locales/en_US/messages.json", "~/Library/Application Support/Firefox/profiles.ini", ] In [57]: magic_test_paths = [ Path(x).expanduser() for x in magic_test_files if Path(x).expanduser().exists() ]

print(magic_test_paths)

[PosixPath('/Applications/Safari.app/Contents/Resources/[email protected]') , PosixPath('/Applications/Keynote.app/Contents/Resources/Acknowledgments.pd f'), PosixPath('/Applications/iTunes.app/Contents/Resources/iTunes.icns'), P osixPath('/Applications/Utilities/Terminal.app/Contents/Resources/Fonts/SFMo no-Bold.otf'), PosixPath('/Users/jeremy/Library/Application Support/Google/ hrome/Local State'), PosixPath('/Users/jeremy/Library/Application Support/Go ogle/Chrome/Default/Extensions/aapocclcgogkmnckokdopfmhonfmgoek/0.10_0/_loca les/en_US/messages.json'), PosixPath('/Users/jeremy/Library/Application Supp ort/Firefox/profiles.ini')] In [62]: magic_test_table = [["File path", "File name", "File type"]] for magic_test_path in magic_test_paths: this_path = magic_test_path this_path_string = str(this_path) this_file_name = PurePosixPath(this_path).name this_file_magic = magic.from_file(this_path_string) magic_test_table.append([this_path_string, this_file_name, this_file_magic]) In [158]: import tabulate display(HTML(tabulate.tabulate(magic_test_table, tablefmt='html')))

File path File name File type PNG image data, 32 x 32, 8- /Applications/Safari.app/Contents/Resources/Certifi[email protected] Certifi[email protected] bit/color RGBA, non- interlaced PDF /Applications/Keynote.app/Contents/Resources/Acknowledgments.pdf Acknowledgments.pdf document, version 1.3 Mac OS X icon, /Applications/iTunes.app/Contents/Resources/iTunes.icns iTunes.icns 101958 bytes, "TOC " type OpenType /Applications/Utilities/Terminal.app/Contents/Resources/Fonts/SFMono-Bold.otf SFMono-Bold.otf font data ASCII text, with very /Users/jeremy/Library/Application Support/Google/Chrome/Local State Local State long lines, with no line terminators YAML In [91]: import os import yaml import pprint In [157]: pp = pprint.PrettyPrinter(indent=4)

if Path("environment.yml").exists: with open("environment.yml") as f: # Disable load() deprecation warning # https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecatio n yaml_data = yaml.load(f, Loader=yaml.FullLoader)

In [93]: print(type(yaml_data))

In [94]: pp.pprint(yaml_data)

{ 'channels': ['conda-forge', 'defaults'], 'dependencies': [ 'pip', 'setuptools', 'jupyter', 'nb_conda', 'openssl', 'certifi', 'jupyter_contrib_nbextensions', 'jupyter_nbextensions_configurator', 'rise', 'cookiecutter', 'black', 'faker', 'pyjokes', 'configparser', 'pyyaml', 'gspread', 'oauth2client', 'df2gspread', In [95]: yaml_data["dependencies"]

Out[95]: ['pip', 'setuptools', 'jupyter', 'nb_conda', 'openssl', 'certifi', 'jupyter_contrib_nbextensions', 'jupyter_nbextensions_configurator', 'rise', 'cookiecutter', 'black', 'faker', 'pyjokes', 'configparser', 'pyyaml', 'gspread', 'oauth2client', 'df2gspread', 'tabulate', CSV In [97]: !python3 -c "import faker; print(dir(faker))"

['Factory', 'Faker', 'Generator', 'VERSION', '__builtins__', '__cached__', ' __doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', ' __spec__', 'config', 'factory', 'generator', 'providers', 'utils'] In [98]: !csvfaker \ -r100 \ first_name \ last_name \ job \ latitude \ longitude \ date_this_year \ mac_platform_token \ mac_address \ ipv4 > resources/random_csv100.txt In [12]: csv_path = "resources/random_csv100.txt"

csv_data = Path("resources/random_csv100.txt").read_text().split("\n") csv_length = len(csv_data) print(csv_data[:5])

['first_name,last_name,job,latitude,longitude,date_this_year,mac_platform_to ken,mac_address,ipv4', 'Natasha,Mendez,Volunteer coordinator,81.8655525,84.3 48209,2019-03-01,Macintosh; Intel Mac OS X 10_8_6,4c:08:93:5f:f4:54,192.5.16 7.24', 'Jillian,Melton,Patent attorney,-3.0106205,-30.527011,2019-04-26,Maci ntosh; Intel Mac OS X 10_8_0,b8:b9:fb:91:cd:5b,192.52.207.167', 'Cheryl,Nels on,IT sales professional,56.144216,-60.755844,2019-03-26,Macintosh; PPC Mac OS X 10_10_0,da:96:19:74:4e:07,198.51.107.106', 'Richard,Myers,Interior and spatial designer,4.933409,-75.879602,2019-01-01,Macintosh; PPC Mac OS X 10_7 _7,c1:34:2f:cc:a0:87,192.3.153.120'] In [14]: import pandas as pd

df = pd.read_csv(csv_path) df_length = len(df) print(f"Data frame: {df_length} lines")

print(f"CSV file: {csv_length} lines")

Data frame: 100 lines CSV file: 102 lines Excel In [15]: excel_path = "resources/random_excel100.xlsx"

with open (excel_path, "wb") as excel_file: df.to_excel(excel_file)

In [54]: !open "resources/random_excel100.xlsx" JSON In [36]: json_path = "resources/random_json100.json"

df.to_json(json_path, orient="records")

In [37]: !head -c72 "resources/random_json100.json"

[{"first_name":"Natasha","last_name":"Mendez","job":"Volunteer coordinat In [38]: import json

with open (json_path, "r") as json_file: json_data = json.load(json_file) In [39]: new_json_path = "resources/random_json100_indent.json" with open(new_json_path, 'w') as json_out_path: json.dump(json_data, json_out_path, indent=4) In [124]: !head "resources/random_json100_indent.json"

[ { "first_name": "Natasha", "last_name": "Mendez", "job": "Volunteer coordinator", "latitude": 81.8655525, "longitude": 84.348209, "date_this_year": "2019-03-01", "mac_platform_token": "Macintosh; Intel Mac OS X 10_8_6", "mac_address": "4c:08:93:5f:f4:54", Google Sheet In [22]: import gspread from oauth2client.service_account import ServiceAccountCredentials In [25]: scope = ['https://spreadsheets.google.com/feeds','https://www.googleapis.com/aut h/drive'] creds = ServiceAccountCredentials.from_json_keyfile_name('PSUMac-007065ab9b1e.js on', scope) client = gspread.authorize(creds)

In [31]: google_sheet_name = "dont_know_what_to_put_here" sheet = client.open(google_sheet_name).sheet1 survey = sheet.get_all_records() print(survey)

------StopIteration Traceback (most recent call last) ~/anaconda3/envs/anaconda3_501/lib/python3.7/site-packages/gspread/client.py in open(self, title) 122 lambda x: x['name'] == title, --> 123 self.list_spreadsheet_files() 124 )

~/anaconda3/envs/anaconda3_501/lib/python3.7/site-packages/gspread/utils.py in finditem(func, seq) 36 """ ---> 37 return next((item for item in seq if func(item))) 38 StopIteration:

During handling of the above exception, another exception occurred:

SpreadsheetNotFound Traceback (most recent call last) in 1 google_sheet_name = "dont_know_what_to_put_here" ----> 2 sheet = client.open(google_sheet_name).sheet1 3 survey = sheet.get_all_records() 4 print(survey)

~/anaconda3/envs/anaconda3_501/lib/python3.7/site-packages/gspread/client.py in open(self, title) 129 return Spreadsheet(self, properties) 130 except StopIteration: --> 131 raise SpreadsheetNotFound 132 133 def open_by_key(self, key):

SpreadsheetNotFound:

Notebook https://notebooks.azure.com/jaharmi/projects/psumacadmins2019-python (https://notebooks.azure.com/jaharmi/projects/psumacadmins2019-python) Questions Thank you Feedback https://bit.ly/psumac2019-372 (https://bit.ly/psumac2019-372)