Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions examples/iniconfig/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# iniconfig Examples

Each sub-directory contains a self-contained example. The order in
which the examples are to appear is specified in `order.json` (an
array of directory names in the expected order).

In each example directory you'll find:

* `config.toml` - must conform to the specification outlined here:
https://docs.pyscript.net/latest/user-guide/configuration/ This is
parsed and ultimately turned into a JSON representation as part of
the package's API object.
* `setup.py` - Python code for contextual and environmental setup,
NOT SEEN BY THE END USER, but is run before the `code.py` code is
evaluated. Allows us to create useful (IPython) shims, avoid
repeating boilerplate and whatnot.
* `code.py` - the actual code added to the editor which forms the
practical example of using the package.
91 changes: 91 additions & 0 deletions examples/iniconfig/iterate_and_handle_errors/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# ---------------------------------------------------------------------
# Iterating an IniConfig and dealing with malformed input.
# ---------------------------------------------------------------------

heading("Iterating sections and items")
note(
"An <code>IniConfig</code> is iterable: looping over it yields "
"sections in the order they appeared in the source. Each "
"section in turn yields its <code>(name, value)</code> pairs "
"via <code>.items()</code>. iniconfig also supports multi-line "
"values -- just indent the continuation lines."
)

ini_source = """
[fiction]
title = The Left Hand of Darkness
author = Ursula K. Le Guin
tags = sci-fi,classic

[non_fiction]
title = The Making of the Atomic Bomb
author = Richard Rhodes

[poetry]
title = Ariel
author = Sylvia Plath
# A multi-line value: indent continuation lines.
notes =
Published posthumously in 1965.
Restored edition followed in 2004.
"""

shelf = iniconfig.IniConfig("shelf.ini", data=ini_source)

# Membership tests work on section names.
note(f"Has 'poetry' section? <code>{'poetry' in shelf}</code>")
note(f"Has 'cookbooks' section? <code>{'cookbooks' in shelf}</code>")

# Walk every section and dump its items as a small HTML list.
rows = []
for section in shelf:
items_html = "".join(
f"<li><code>{name}</code> = {value!r}</li>"
for name, value in section.items()
)
rows.append(f"<h3>[{section.name}]</h3><ul>{items_html}</ul>")
display(HTML("".join(rows)), append=True)

# ---------------------------------------------------------------------
# Parse errors carry the line number, which makes them easy to
# surface to a user.
# ---------------------------------------------------------------------
heading("Parse errors point at the offending line")
note(
"If the source is malformed, iniconfig raises "
"<code>ParseError</code> with the file label and line number. "
"The example below has a stray key outside any section."
)

broken_source = """
[ok]
key = value

stray_key = oops, no section above me

[also_ok]
foo = bar
"""

try:
iniconfig.IniConfig("broken.ini", data=broken_source)
except iniconfig.ParseError as err:
note(
f"Caught <code>ParseError</code>: <code>{err}</code>"
)

# Duplicate section names are also rejected.
duplicate_source = """
[server]
host = a

[server]
host = b
"""

try:
iniconfig.IniConfig("dup.ini", data=duplicate_source)
except iniconfig.ParseError as err:
note(
f"Duplicate sections raise too: <code>{err}</code>"
)
1 change: 1 addition & 0 deletions examples/iniconfig/iterate_and_handle_errors/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
packages = ["iniconfig"]
22 changes: 22 additions & 0 deletions examples/iniconfig/iterate_and_handle_errors/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""Lighter setup for the second example: same names, no IPython shim."""
import js
from pyscript import window, HTML, display as _display

js.alert = window.alert


def display(*args, **kwargs):
return _display(
*args, **kwargs, target=__pyscript_display_target__,
)


def heading(text, level=2):
display(HTML(f"<h{level}>{text}</h{level}>"), append=True)


def note(text):
display(HTML(f"<p>{text}</p>"), append=True)


import iniconfig
4 changes: 4 additions & 0 deletions examples/iniconfig/order.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[
"parse_ini_basics",
"iterate_and_handle_errors"
]
60 changes: 60 additions & 0 deletions examples/iniconfig/parse_ini_basics/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""
A first look at iniconfig: parsing a small INI file in memory.

iniconfig is a tiny, dependency-free parser that preserves the order
of sections and entries, supports `#` comments, and reports proper
line numbers on errors. See https://github.com/pytest-dev/iniconfig
for the source.

Normally you'd call `IniConfig("path/to/file.ini")`, which reads the
file from disk. We can also pass the source text directly using the
`data` argument, which is handy when the content lives in memory --
as it does here in the browser.
"""
from IPython.core.display import display, HTML

import iniconfig

heading("A made-up app config")
note(
"Imagine the INI text below lives in <code>app.ini</code> on "
"disk. We'll parse it and pull values out by section and key."
)

ini_source = """
# content of app.ini
[server] # web server settings
host = localhost
port = 8080

[database]
url = sqlite:///app.db
pool_size = 5

[features]
# comma-separated flags, parsed below
enabled = search,export,dark_mode
"""

# Pass the source via `data=`; the first arg is just a label used in
# error messages.
config = iniconfig.IniConfig("app.ini", data=ini_source)

# Index a section like a dict, then index a key like a dict.
note(f"Server host: <code>{config['server']['host']}</code>")
note(f"Server port: <code>{config['server']['port']}</code>")
note(f"Database URL: <code>{config['database']['url']}</code>")

# `get` lets you supply a default and a converter function; perfect
# for splitting CSV-style values or coercing to int.
enabled = config.get(
"features", "enabled", default=[], convert=lambda x: x.split(","),
)
pool = config.get("database", "pool_size", default=1, convert=int)

note(f"Enabled features: <code>{enabled}</code>")
note(f"Pool size (as int): <code>{pool}</code> (type: {type(pool).__name__})")

# `get` with a missing key returns the default rather than raising.
missing = config.get("server", "timeout", default=30, convert=int)
note(f"Missing key falls back to default: <code>{missing}</code>")
1 change: 1 addition & 0 deletions examples/iniconfig/parse_ini_basics/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
packages = ["iniconfig"]
42 changes: 42 additions & 0 deletions examples/iniconfig/parse_ini_basics/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""
Shim IPython's display API onto PyScript so example code written in a
Jupyter/IPython idiom runs unmodified in the browser.
"""

import sys
import types
import js
from pyscript import window, HTML, display as _display

js.alert = window.alert


def display(*args, **kwargs):
"""Wrap pyscript.display so output lands in the example target."""
return _display(
*args, **kwargs, target=__pyscript_display_target__,
)


ipython = types.ModuleType("IPython")
core = types.ModuleType("IPython.core")
core_display = types.ModuleType("IPython.core.display")
core_display.display = display
core_display.HTML = HTML
ipython.core = core
core.display = core_display
ipython.get_ipython = lambda: None
ipython.display = core_display
sys.modules["IPython"] = ipython
sys.modules["IPython.core"] = core
sys.modules["IPython.core.display"] = core_display
sys.modules["IPython.display"] = core_display


def heading(text, level=2):
display(HTML(f"<h{level}>{text}</h{level}>"), append=True)


def note(text):
display(HTML(f"<p>{text}</p>"), append=True)