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
46 changes: 46 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# AGENTS.md — deformdemo

Run these checks before considering work done. They mirror `.github/workflows/ci-tests.yml`
(invoked via `tox`). All commands run from the repo root.

## Fast pre-check (run first)

```sh
ruff check # line-length 79, excludes bootstrap.py (config in pyproject.toml)
```

`bootstrap.py` is a generated zc.buildout script (Zope Foundation); it is outside the CI
lint scope (CI lints `deformdemo setup.py`, not the repo-root bootstrap script) and is
excluded from ruff.

## Lint (CI: `tox -e lint`, basepython python3.11)

```sh
flake8 deformdemo setup.py
isort --check-only --diff deformdemo setup.py
black --check --diff deformdemo setup.py
python setup.py check -r -s -m
check-manifest
```

- `black` / `isort` / `flake8` line-length: **79** (see `pyproject.toml` `[tool.black]`, `[tool.isort]`, `.flake8`).
- Lint scope is the `deformdemo/` package **and** `setup.py` (not `bootstrap.py`).

## Tests (CI: `tox -e py`)

Functional/Selenium tests run against a live server via `tox.sh`:

```sh
pserve demo.ini & # started by tox.sh
pytest "$@" # functional suite (needs selenium + Firefox; URL env set by CI)
```

Requires a running Selenium standalone-firefox (CI sets `URL=http://<host>:8523`,
`DISPLAY`, `WEBDRIVER`). Skip unless working on functional tests.

## Notes

- Supported Python: 3.10–3.13 + PyPy 3.10 (see CI matrix).
- `deformdemo` depends on `deform` from git (`requirements-dev.txt`); changes to deform
often need a matching demo change here.
- `setup.cfg [tool:pytest]` uses `python_files = test.py`, `testpaths = .`, `addopts = -W always`.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ include *.py
include *.sh
include *.toml
include *.txt
include AGENTS.md
include .isort.cfg .flake8 .dockerignore
include buildout.cfg
include demo.ini
Expand Down
66 changes: 33 additions & 33 deletions deformdemo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
capabilities and which provides a functional test suite"""

import csv
import datetime
import decimal
import inspect
import logging
Expand Down Expand Up @@ -100,7 +101,12 @@ def render_form(
readonly=False,
is_i18n=False,
):
if getattr(form, "use_ajax", False):
if not form.action:
form.action = self.request.path

captured = None
validation_failed = False

if submitted in self.request.POST:
# the request represents a form submission
Expand All @@ -116,13 +122,20 @@ def render_form(
except deform.ValidationFailure as e:
# the submitted values could not be validated
html = e.render()
validation_failed = True

else:
# the request requires a simple form rendering
html = form.render(appstruct, readonly=readonly)

if self.request.is_xhr:
return Response(html)
# An AJAX (use_ajax) request wants just the form fragment, which
# replaces the form in place. On validation failure, respond with
# HTTP 422 so the client knows to re-render with errors.
is_submission = submitted in self.request.POST
is_ajax_submit = is_submission and getattr(form, "use_ajax", False)
if self.request.is_xhr or is_ajax_submit:
status = 422 if validation_failed else 200
return Response(html, status=status)

code, start, end = self.get_code(2)
locale_name = get_locale_name(self.request)
Expand Down Expand Up @@ -159,6 +172,19 @@ def get_code(self, level):
code = unicode(code, "utf-8")
return highlight(code, PythonLexer(), formatter), start, end

def code_for(self, *method_names):
"""Return highlighted source for one or more view methods by name,
for display in a demo's "Code" panel."""
chunks = []
for name in method_names:
method = getattr(type(self), name, None)
if method is None:
continue
src = inspect.getsource(method)
chunks.append(src)
code = "\n".join(chunks)
return highlight(code, PythonLexer(), formatter)

@view_config(name="thanks.html")
def thanks(self):
return Response(
Expand Down Expand Up @@ -969,36 +995,22 @@ class Schema(colander.Schema):
mapping = Mapping()

schema = Schema()
options = """
{success:
function (rText, sText, xhr, form) {
var loc = xhr.getResponseHeader('X-Relocate');
if (loc) {
document.location = loc;
};
}
}
"""

def succeed():
location = self.request.resource_url(
self.request.context, "thanks.html", route_name="deformdemo"
)
# To appease jquery 1.6+, we need to return something that smells
# like HTML, or we get a "Node cannot be inserted at the
# specified point in the hierarchy" Javascript error. This didn't
# used to be required under JQuery 1.4.
# The AJAX client (deform's vanilla JS) follows the X-Redirect
# response header for a client-side redirect on success.
return Response(
"<div>hurr</div>",
"",
headers=[
("X-Relocate", location),
("X-Redirect", location),
("Content-Type", "text/html"),
],
)

form = deform.Form(
schema, buttons=("submit",), use_ajax=True, ajax_options=options
)
form = deform.Form(schema, buttons=("submit",), use_ajax=True)

return self.render_form(form, success=succeed)

Expand Down Expand Up @@ -1056,7 +1068,6 @@ class Schema(colander.Schema):
@view_config(renderer="templates/form.pt", name="sequence_of_dateinputs")
@demonstrate("Sequence of Date Inputs")
def sequence_of_dateinputs(self):
import datetime

class Sequence(colander.SequenceSchema):
date = colander.SchemaNode(
Expand All @@ -1077,7 +1088,6 @@ class Schema(colander.Schema):
@view_config(renderer="templates/form.pt", name="sequence_of_i18n")
@demonstrate("Sequence of I18N")
def sequence_of_i18n(self):
import datetime

locale_name = get_locale_name(self.request)

Expand Down Expand Up @@ -1425,7 +1435,6 @@ class Schema(colander.Schema):
@view_config(renderer="templates/form.pt", name="dateparts")
@demonstrate("Date Parts Widget")
def dateparts(self):
import datetime

class Schema(colander.Schema):
date = colander.SchemaNode(
Expand All @@ -1445,7 +1454,6 @@ class Schema(colander.Schema):
@view_config(renderer="templates/form.pt", name="dateparts_readonly")
@demonstrate("Date Parts Widget (read-only)")
def dateparts_readonly(self):
import datetime

class Schema(colander.Schema):
date = colander.SchemaNode(
Expand All @@ -1463,7 +1471,6 @@ class Schema(colander.Schema):
@view_config(renderer="templates/form.pt", name="dateinput")
@demonstrate("Date Input Widget")
def dateinput(self):
import datetime

class Schema(colander.Schema):
somedate = colander.SchemaNode(
Expand All @@ -1483,7 +1490,6 @@ class Schema(colander.Schema):
@view_config(renderer="templates/form.pt", name="timeinput")
@demonstrate("Time Input")
def timeinput(self):
import datetime

class Schema(colander.Schema):
sometime = colander.SchemaNode(
Expand All @@ -1503,7 +1509,6 @@ class Schema(colander.Schema):
@view_config(renderer="templates/form.pt", name="datetimeinput")
@demonstrate("DateTime Input Widget")
def datetimeinput(self):
import datetime

class Schema(colander.Schema):
date_time = colander.SchemaNode(
Expand Down Expand Up @@ -1531,7 +1536,6 @@ class Schema(colander.Schema):
@view_config(renderer="templates/form.pt", name="datetimeinput_readonly")
@demonstrate("DateTime Input Widget (read-only)")
def datetimeinput_readonly(self):
import datetime

then = datetime.datetime(2011, 5, 5, 1, 2)

Expand All @@ -1549,7 +1553,6 @@ class Schema(colander.Schema):
@view_config(renderer="templates/form.pt", name="edit")
@demonstrate("Edit Form")
def edit(self):
import datetime

class Mapping(colander.Schema):
name = colander.SchemaNode(
Expand Down Expand Up @@ -2642,7 +2645,6 @@ class Schema(colander.Schema):
@view_config(renderer="templates/form.pt", name="deferred_schema_bindings")
@demonstrate("Deferred Schema Bindings")
def deferred_schema_bindings(self):
import datetime

import colander

Expand Down Expand Up @@ -2830,7 +2832,6 @@ class Values(colander.Schema):
@view_config(renderer="templates/form.pt", name="readonly_argument")
@demonstrate("Readonly Widget Argument")
def readonly_argument(self):
import datetime

class Schema(colander.Schema):
textinput = colander.SchemaNode(
Expand Down Expand Up @@ -3006,7 +3007,6 @@ class Schema(colander.Schema):
)
@demonstrate("Custom classes on outermost html element of Widgets")
def custom_classes_on_outermost_html_element(self):
import datetime

class Mapping(colander.Schema):
upload = colander.SchemaNode(
Expand Down
7 changes: 1 addition & 6 deletions deformdemo/templates/main.pt
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,14 @@
</tal:block>

<!-- JavaScript -->
<script src="${request.static_url('deform:static/scripts/jquery-2.0.3.min.js')}"
<script src="${request.static_url('deform:static/scripts/deform.js')}"
type="text/javascript"></script>
<script src="${request.static_url('deform:static/scripts/bootstrap.bundle.min.js')}"
type="text/javascript"></script>
<tal:block repeat="reqt js_links|[]">
<script type="text/javascript" src="${request.static_url(reqt)}"></script>
</tal:block>

<script>
// This forces us to use JS date time picker polyfill so that our functional tests can run properly regardless of a browser
window.forceDateTimePolyfill = true;
</script>

<tal:slot metal:define-slot="head_end"></tal:slot>

</head>
Expand Down
4 changes: 2 additions & 2 deletions deformdemo/templates/mini.pt
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
</tal:block>

<!-- JavaScript -->
<script src="${request.static_url('deform:static/scripts/jquery-2.0.3.min.js')}"
<script src="${request.static_url('deform:static/scripts/deform.js')}"
type="text/javascript"></script>
<script src="${request.static_url('deform:static/scripts/bootstrap.min.js')}"
<script src="${request.static_url('deform:static/scripts/bootstrap.bundle.min.js')}"
type="text/javascript"></script>
<tal:block repeat="reqt js_links|[]">
<script type="text/javascript" src="${request.static_url(reqt)}"></script>
Expand Down
9 changes: 5 additions & 4 deletions deformdemo/templates/popup_example.pt
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
<script>
"use strict";

$(document).ready(function() {
// Automatically show pop up if the form contains validation errors
if($("#my-pop-up .error-msg-detail").length > 0) {
$("#my-pop-up").modal("show");
// Automatically show the pop up if the form contains validation errors.
document.addEventListener("DOMContentLoaded", function () {
var popup = document.getElementById("my-pop-up");
if (popup && popup.querySelector(".error-msg-detail")) {
bootstrap.Modal.getOrCreateInstance(popup).show();
}
});
</script>
Expand Down
Loading
Loading