Background
PR #1457 landed public typed-schema (graphistry.schema.NodeType / EdgeType / GraphSchema / EdgeTopology, bind(schema=...), local gfql_validate()). The bound schema is stored on Plottable._gfql_schema (private attribute, set via bind(schema=...)).
There is no public accessor. Consumers (AI synthesizers, graphistrygpt, schema-aware tooling, debugging code) must reach for getattr(g, "_gfql_schema", None) against a private attribute. This is a P0 ergonomics gap surfaced by the AI-synthesizer user-testing exercise.
Goal
Add a public read-only accessor for bound GraphSchema so consumers can introspect without touching private attributes.
Scope
- Add
Plottable.schema (or Plottable.get_schema() — pick whichever fits existing conventions) as a read-only property returning the bound GraphSchema or None if no schema is bound.
- Add
Plottable.has_schema() -> bool as a convenience predicate.
- Re-export via top-level
graphistry namespace if appropriate per the existing accessor patterns.
- Document the new accessor; mark experimental per
#1457's experimental marking.
- Anchored regression tests: bound → returns schema; unbound → returns None; has_schema returns matching bool.
Non-scope
- No changes to
bind(schema=...) behavior or internal storage.
- No schema mutation API (read-only accessor only).
- No inference (separate
#1338 lane).
- No pretty-printing (separate follow-on issue).
Suggested signature
class Plottable:
@property
def schema(self) -> Optional[GraphSchema]:
"""Read-only access to bound GraphSchema (or None if unbound).
Experimental per #1457.
"""
return getattr(self, "_gfql_schema", None)
def has_schema(self) -> bool:
"""True if a GraphSchema is bound on this Plottable."""
return self._gfql_schema is not None
Acceptance
g.schema returns the bound GraphSchema instance (or None)
g.has_schema() returns matching bool
- Public docs reflect the accessor
- Anchored tests cover bound + unbound cases
- Experimental marking preserved
- Compiler-plan surface touched: no
Cross-refs
- Landed in
#1457 / #1337
- Surfaced by AI-synthesizer user-testing 2026-05-25 (P0 finding)
- Coordination: pygraphistry2/'s
#1338 inference work; this accessor returns whatever schema is bound (declared or inferred)
- Metaissue:
#1058, #1046
- Downstream consumer: graphistrygpt/'s
plugins/graphistry/tool.py needs this to retire _chain_contains_radial_layout-style brittle string-matching code
Effort
Small (~30 prod LOC). Drop-in fit for any paused pygraphistry lane.
Background
PR
#1457landed public typed-schema (graphistry.schema.NodeType / EdgeType / GraphSchema / EdgeTopology,bind(schema=...), localgfql_validate()). The bound schema is stored onPlottable._gfql_schema(private attribute, set viabind(schema=...)).There is no public accessor. Consumers (AI synthesizers, graphistrygpt, schema-aware tooling, debugging code) must reach for
getattr(g, "_gfql_schema", None)against a private attribute. This is a P0 ergonomics gap surfaced by the AI-synthesizer user-testing exercise.Goal
Add a public read-only accessor for bound
GraphSchemaso consumers can introspect without touching private attributes.Scope
Plottable.schema(orPlottable.get_schema()— pick whichever fits existing conventions) as a read-only property returning the boundGraphSchemaorNoneif no schema is bound.Plottable.has_schema() -> boolas a convenience predicate.graphistrynamespace if appropriate per the existing accessor patterns.#1457's experimental marking.Non-scope
bind(schema=...)behavior or internal storage.#1338lane).Suggested signature
Acceptance
g.schemareturns the boundGraphSchemainstance (orNone)g.has_schema()returns matching boolCross-refs
#1457/#1337#1338inference work; this accessor returns whatever schema is bound (declared or inferred)#1058,#1046plugins/graphistry/tool.pyneeds this to retire_chain_contains_radial_layout-style brittle string-matching codeEffort
Small (~30 prod LOC). Drop-in fit for any paused pygraphistry lane.