{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "92934cb0",
   "metadata": {},
   "source": [
    "# File Management"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "395340d6",
   "metadata": {
    "tags": [
     "hide-input",
     "thebe-init"
    ]
   },
   "outputs": [],
   "source": [
    "import sys\n",
    "from pathlib import Path\n",
    "\n",
    "current = Path.cwd()\n",
    "for parent in [current, *current.parents]:\n",
    "    if (parent / '_config.yml').exists():   ### root\n",
    "        project_root = parent               ### Add project root\n",
    "        break\n",
    "else:\n",
    "    project_root = Path.cwd().parent.parent\n",
    "\n",
    "sys.path.insert(0, str(project_root))\n",
    "\n",
    "from shared import thinkpython, diagram, jupyturtle, download, structshape\n",
    "\n",
    "# Register as top-level modules so direct imports work in subsequent cells\n",
    "sys.modules['thinkpython'] = thinkpython\n",
    "sys.modules['diagram'] = diagram\n",
    "sys.modules['jupyturtle'] = jupyturtle\n",
    "sys.modules['download'] = download\n",
    "sys.modules['structshape'] = structshape"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "contents",
   "metadata": {},
   "source": [
    "```{contents}\n",
    ":local:\n",
    ":depth: 2\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4fb16aab",
   "metadata": {},
   "source": [
    "## Download Files"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80cffd46",
   "metadata": {},
   "source": [
    "## Custom Code \n",
    "If you download the notebook files and place them in a folder in your project directory, or if you write your own notebooks and want to use features such as `jupyturtle`, you may need additional helper files. The cell below downloads the files used specifically for this book. You don't need to understand this code yet, but you can see that it downloads several `*.py` files from Dr. Allen Downey's GitHub repository to enable the required functionality.\n",
    "\n",
    "You then create a folder in your project directory called `shared`, place the downloaded files in it, and add an empty file called `__init__.py`, which makes the `shared` folder a package. Later, in any project notebook, you can import modules from that package and use their functions:\n",
    "\n",
    "```python\n",
    "from shared import [module]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2f44cff9",
   "metadata": {},
   "source": [
    "(download-function)=\n",
    "**The download function**:\n",
    "\n",
    "```python\n",
    "from os.path import basename, exists\n",
    "\n",
    "def download(url):\n",
    "    filename = basename(url)\n",
    "    if not exists(filename):\n",
    "        from urllib.request import urlretrieve\n",
    "\n",
    "        local, _ = urlretrieve(url, filename)\n",
    "        print(\"Downloaded \" + str(local))\n",
    "    return filename\n",
    "```\n",
    "\n",
    "- thinkpython: 'https://github.com/AllenDowney/ThinkPython/raw/v3/thinkpython.py'\n",
    "- diagram: 'https://github.com/AllenDowney/ThinkPython/raw/v3/diagram.py'\n",
    "- jupyturtle: 'https://github.com/ramalho/jupyturtle/releases/download/2024-03/jupyturtle.py'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1ddefa76",
   "metadata": {},
   "source": [
    "To use the downloaded files as modules, create a directory named `shared` in the project root and place the files in it. In each notebook that needs these modules, put the code snippet below at the beginning of the notebook.\n",
    "\n",
    "```python\n",
    "import sys\n",
    "from pathlib import Path\n",
    "\n",
    "current = Path.cwd()\n",
    "for parent in [current, *current.parents]:\n",
    "    if (parent / '_config.yml').exists():\n",
    "        project_root = parent  # ← Add project root, not chapters\n",
    "        break\n",
    "else:\n",
    "    project_root = Path.cwd().parent.parent\n",
    "\n",
    "sys.path.insert(0, str(project_root))\n",
    "\n",
    "from shared import thinkpython, diagram, jupyturtle\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ctx-mgr-intro-0501",
   "metadata": {},
   "source": [
    "## The `with` Statement\n",
    "\n",
    "Before writing to or reading from a file, Python needs to *open* it — and when you're done, the file must be *closed* to flush data and release system resources. Forgetting to close a file can cause data loss or resource leaks.\n",
    "\n",
    "The `with` statement handles this automatically:\n",
    "\n",
    "```python\n",
    "with open('data.txt') as f:\n",
    "    content = f.read()\n",
    "# file is closed here automatically, even if an error occurred inside the block\n",
    "```\n",
    "\n",
    "This is equivalent to:\n",
    "\n",
    "```python\n",
    "f = open('data.txt')\n",
    "try:\n",
    "    content = f.read()\n",
    "finally:\n",
    "    f.close()          # runs even if an exception is raised\n",
    "```\n",
    "\n",
    "Any object that supports the `with` statement is called a **context manager**. `open()` returns one. The `with` block guarantees that setup (opening) and teardown (closing) happen in the right order, even if something goes wrong.\n",
    "\n",
    ":::{note}\n",
    "You will see `with` used throughout this course with files, database connections, network sockets, and more. Ch13 explains how to build your own context managers.\n",
    ":::\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "5719ee1d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, this is the first line of a new text file.\n",
      "This is the second line.\n"
     ]
    }
   ],
   "source": [
    "text = \"\"\"Hello, this is the first line of a new text file.\n",
    "This is the second line.\"\"\"\n",
    "\n",
    "with open('../../../data/text_file.txt', 'w') as f:\n",
    "    f.write(text)\n",
    "\n",
    "with open('../../../data/text_file.txt') as f:\n",
    "    file_content = f.read()       ### file object\n",
    "    print(file_content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25890ce8",
   "metadata": {},
   "source": [
    "## `Path()`\n",
    "\n",
    "The `Path()` function is from the pathlib module — the modern way to work with file paths in Python. To use it, you \n",
    "\n",
    "- import `Path()` from Python's pathlib module\n",
    "- build the path pointing to the directory or to the file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "e49f6f07",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "data = project_root / 'data'                  ### path of data folder\n",
    "words_path = project_root / 'data' / 'words.txt'   ### path of words.txt file"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c7b26fee",
   "metadata": {},
   "source": [
    "After you have the path object, you can do many things with it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4f0f5bcf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Building paths (works on Windows and Mac/Linux)\n",
    "f = data / \"words.txt\"          ### use data folder and file name to build path\n",
    "\n",
    "# File info\n",
    "print(f.name)          # \"words.txt\"\n",
    "print(f.stem)          # \"words\"\n",
    "print(f.suffix)        # \".txt\"\n",
    "print(data.parent)     # parent directory of data/\n",
    "\n",
    "# Checking existence\n",
    "print(f.exists())\n",
    "print(f.is_file())\n",
    "print(f.is_dir())\n",
    "\n",
    "# Reading and writing\n",
    "### uncomment the following line to read the file\n",
    "### the following line will throw an error if the file does not exist\n",
    "# print(f.read_text())\n",
    "tmp = Path('tmp_example.txt')\n",
    "tmp.write_text(\"hello\")         # write to a temp file, not words.txt\n",
    "tmp.unlink()                    # clean up\n",
    "\n",
    "# Current directory and home\n",
    "print(Path.cwd())      # current working directory\n",
    "print(Path.home())     # home directory (~)\n",
    "\n",
    "# Listing files\n",
    "print(list(data.iterdir()))            # all files in folder\n",
    "print(list(data.glob(\"*.csv\")))        # all CSVs\n",
    "print(list(data.rglob(\"*.txt\")))       # recursive"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b817a0c3",
   "metadata": {},
   "source": [
    "An example of using `Path()`:\n",
    "\n",
    "1. Import `Path()`\n",
    "2. Build the path\n",
    "3. Check to see if file exists. If not, do something."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "433829ca",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "words_path = project_root / 'data' / 'words.txt'\n",
    "if not words_path.exists():\n",
    "    download('https://raw.githubusercontent.com/AllenDowney/ThinkPython/v3/words.txt', words_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e554316",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
