Skip to content

Conversation

@Karrenbelt
Copy link
Collaborator

@Karrenbelt Karrenbelt commented Mar 22, 2025

A re-implementation of the protocol scaffolder

return super().__new__(cls, to_float32(float(value)))


class Int32(BaseConstrainedInt):
Copy link
Collaborator Author

@Karrenbelt Karrenbelt Apr 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need this, not to constrain the data per se (pydantic can deal with that via i.e. conint), but to encode to the correct proto type; the information from i.e. conint does not carry over to the python int generated, so in many cases you cannot discern which type of proto integer it was / needs to be converted into

Comment on lines 21 to 34
class ResolvedType(NamedTuple):
fully_qualified_name: str
ast_node: MessageAdapter | ast.Enum | None = None

@property
def is_enum(self):
return isinstance(self.ast_node, ast.Enum)

@property
def is_message(self):
return isinstance(self.ast_node, MessageAdapter)

def __str__(self):
return self.fully_qualified_name
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This facilitates checking, as we traverse parents, what type we're dealing with

Comment on lines +27 to +28
for cls in BaseModel.__subclasses__():
cls.model_rebuild()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sames goes for this module: the performative models (generated under tests/performatives.py) also need to update forward references in order for hypothesis to generate strategies from them correctly using st.from_type

Comment on lines +130 to +152
# Point PYTHONPATH to the temporary project root so generated modules are discoverable
env = os.environ.copy()
env["PYTHONPATH"] = str(repo_root)

command = ["adev", "-v", "scaffold", "protocol", str(protocol_spec)]
result = subprocess.run(command, env=env, check=False, text=True, capture_output=True)
if result.returncode != 0:
msg = f"Protocol scaffolding failed: {result.stderr}"
raise ValueError(msg)

assert protocol_outpath.exists()

test_dir = protocol_outpath / "tests"
command = ["pytest", str(test_dir), "-vv", "-s", "--tb=long", "-p", "no:warnings"]
result = subprocess.run(
command,
env=env,
check=False,
text=True,
capture_output=True,
)

assert result.returncode == 0, f"Failed pytest on generated protocol: {result.stderr}"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before, i passed the env with custom PYTHONPATH only to the last subprocess call and tests passed. However, no code was generated in the proper location (!) as I didn't pass the custom env to the adev -v scaffold protocol call. This resulted in a situation where no tests were ran in the subprocess, causing this test to pass without actually running any tests. We should find a better way to assert this, i.e. by checking output of the subprocess call and comparing that reported output (parsing stdout) to expected result. For now, tho, this assert protocol_outpath.exists() ensures that the expected outpath was generated (which fails when one does not pass the env)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the subprocess task thing for this quite extensively used throughout the repo

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which gives you access to all the env var stuff etc and provides the abstraction

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there’s a small mix-up in how loading and executing (newly generated) code occurs in the context of an isolated_filesystem:

  1. We run the codegen inside an isolated temp directory (e.g. /tmp/...) that isn’t on the default PYTHONPATH.
  2. Spawning a separate python process guarantees zero cached imports; otherwise you’ll end up with stale code (cache invalidation truly is one of the two hard things in computer science - more details in isolated_filesystem contextmanager issues #609)
  3. When you switch into the temp directory, you must also update PYTHONPATH (or use -m with the correct module path) so that the newly written files become importable in that subprocess.

protocol_name = self.protocol.metadata.name
protocol_author = self.protocol.metadata.author
speech_acts = list(self.protocol.metadata.speech_acts)
roles = list(self.protocol.interaction_model.roles)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isnt a key on the original protocol specs?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well this is the custom data model for a protocol spec consisting of 3 yaml parts. I've simply named these individual sections (as per the pydantic model here)

exclude = "/(\n \\.eggs\n | \\.git\n | \\.hg\n | \\.mypy_cache\n | \\.tox\n | \\.venv\n | _build\n | buck-out\n | build\n | dist\n)/\n"

[tool.poetry.dependencies]
python = ">=3.10,<3.14"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why change this?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do no want to drop this support

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
Bro there is like 200 commits here.

It doesnt even show on this page.

openapi-spec-validator = "0.2.8"
disutils = "^1.4.32.post2"
setuptools = "^75.8.0"
proto-schema-parser = "^1.5.0"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this enforces less than 3.13< ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I think the bump to include 3.13 previously was an error in a previous PR of mine (see git blame)



@pytest.fixture(scope="module")
def module_scoped_dummy_agent_tim() -> Path:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so now there is like 3 different agents?

Why?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because of the time it takes to create this "isolated_filesystem". See the difference in run times (1 and 3) in this comment

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not remove the unused ones?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants