Skip to content

Commit

Permalink
Merge pull request #113 from Confidenceman02/release
Browse files Browse the repository at this point in the history
Prepare release 0.15.0
  • Loading branch information
Confidenceman02 authored Jul 15, 2024
2 parents 34a04ac + cc5e6ce commit 7701673
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 21 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## [0.15.0] - 2024-07-15

### Added

- FindPrograms strategy

```elm
python manage.py djelm findprograms <djelm_app>
```

## [0.14.0] - 2024-06-25

### Fixed
Expand Down Expand Up @@ -240,6 +250,7 @@ type alias A_B__

- First version to pyPI

[0.15.0]: https://github.com/Confidenceman02/django-elm/compare/0.14.0...0.15.0
[0.14.0]: https://github.com/Confidenceman02/django-elm/compare/0.13.0...0.14.0
[0.13.0]: https://github.com/Confidenceman02/django-elm/compare/0.12.0...0.13.0
[0.12.0]: https://github.com/Confidenceman02/django-elm/compare/0.11.0...0.12.0
Expand Down
13 changes: 12 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins = [ "mypy_django_plugin.main" ]

[tool.poetry]
name = "djelm"
version = "0.14.0"
version = "0.15.0"
description = "A framework for using Elm programs in a Django project"
authors = ["Confidenceman02"]
readme = "README_pypi.md"
Expand Down Expand Up @@ -34,6 +34,7 @@ python = "^3.11"
django = ">=4.2.5"
watchfiles = "^0.21.0"
pydantic = "^2.4.2"
aiofiles = "^24.1.0"

[tool.poetry.group.test.dependencies]
pytest = "^7.4.2"
Expand Down
6 changes: 4 additions & 2 deletions src/djelm/generators.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
import shutil
from typing import Protocol, Sequence
from typing import Protocol, Sequence, cast
from typing_extensions import TypedDict

from djelm.cookiecutter import CookieCutter
Expand Down Expand Up @@ -287,7 +287,9 @@ def install_elm_deps(self, working_dir: str, logger):
def cookie_cutter(
self, app_name: str, program_name: str, src_path: str, version: str
) -> CookieCutter:
return widget_cookie_cutter(app_name, src_path, program_name, version)
return widget_cookie_cutter(
app_name, src_path, cast(WIDGET_NAMES_T, program_name), version
)

def applicators(
self,
Expand Down
4 changes: 4 additions & 0 deletions src/djelm/management/commands/djelm.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
CompileStrategy,
CreateStrategy,
ElmStrategy,
FindPrograms,
GenerateModelStrategy,
ListStrategy,
ListWidgetsStrategy,
Expand All @@ -31,6 +32,7 @@ class Command(LabelCommand):
generatemodel <app-name> [args].. - generate a model for the existing program <program-name> in the <app-name> app
list - to list all your djelm apps
listwidgets - to list all supported widget programs
findprograms - to list all Elm programs in src/
compile <app-name> - to compile all your elm programs in the given <app-name> app
compilebuild <app-name> - to compile all your elm programs with a production level build in the given <app-name> app
Usage example:
Expand All @@ -43,6 +45,7 @@ class Command(LabelCommand):
python manage.py djelm generatemodel djelm_app MyElmProgram
python manage.py djelm list
python manage.py djelm listwidgets
python manage.py djelm findprograms djelm_app
python manage.py djelm compile djelm_app
python manage.py djelm compilebuild djelm_app
"""
Expand All @@ -58,6 +61,7 @@ class Command(LabelCommand):
| GenerateModelStrategy
| CompileStrategy
| AddWidgetStrategy
| FindPrograms
)

def __init__(self, *args, **kwargs):
Expand Down
64 changes: 62 additions & 2 deletions src/djelm/strategy.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import asyncio
import re
import aiofiles
import os
import shutil
import subprocess
Expand Down Expand Up @@ -67,6 +69,61 @@ class StrategyError(Exception):
pass


@dataclass(slots=True)
class FindPrograms:
app_name: str

def run(self, logger) -> ExitSuccess[list[str]] | ExitFailure[None, StrategyError]:
src_path = get_app_src_path(self.app_name)

if src_path.tag != "Success":
raise src_path.err
if not os.path.isdir(os.path.join(src_path.value, "src")):
logger.write("No programs found.")
return ExitSuccess([])

programs_home = os.path.join(src_path.value, "src")

dir_data: Iterable[tuple[str, list[str], list[str]]] = walk_level(programs_home)
_, _, files = next(dir_data)
elm_files = list(filter(lambda x: x.endswith(".elm"), files))
parsed_file_results = asyncio.run(self.parsed_files(programs_home, elm_files))

if parsed_file_results:
logger.write("I found the following programs:\n")
for f in parsed_file_results:
if f.tag == "Success":
if f.value.endswith(".elm"):
logger.write(f""" {f.value}""")
else:
logger.write(str(f.err))

return ExitSuccess(elm_files)

async def parsed_files(self, programs_home: str, files: list[str]):
parsed_file_exits = [self.parse_file(programs_home, f) for f in files]
return await asyncio.gather(*parsed_file_exits)

async def parse_file(
self, programs_home: str, file: str
) -> ExitSuccess[str] | ExitFailure[None, Exception]:
try:
handle = await aiofiles.open(os.path.join(programs_home, file))
content = await handle.read()
await handle.close()
if re.search(
r"^(main : Program Value Model Msg)", content, flags=re.MULTILINE
):
return ExitSuccess(file)
else:
return ExitFailure(
None, Exception("'main : Program Value Model Msg' not found")
)

except Exception as err:
return ExitFailure(None, err)


@dataclass(slots=True)
class AddWidgetStrategy:
"""Add a djelm widget"""
Expand Down Expand Up @@ -520,11 +577,11 @@ def run(


class ListStrategy:
_apps: list[str] = settings.INSTALLED_APPS
apps: list[str] = settings.INSTALLED_APPS

def run(self, logger) -> ExitSuccess[list[str]] | ExitFailure[None, StrategyError]:
app_path_exits = filterfalse(
lambda x: x.tag == "Failure", map(get_app_path, self._apps)
lambda x: x.tag == "Failure", map(get_app_path, self.apps)
)

dir_data: Iterable[tuple[str, list[str], list[str]]] = map(
Expand Down Expand Up @@ -618,6 +675,7 @@ def create(
| GenerateModelStrategy
| CompileStrategy
| AddWidgetStrategy
| FindPrograms
):
e = Validations().acceptable_command(labels)
match e:
Expand Down Expand Up @@ -658,6 +716,8 @@ def create(
return ListStrategy()
case ExitSuccess(value={"command": "listwidgets"}):
return ListWidgetsStrategy()
case ExitSuccess(value={"command": "findprograms", "app_name": app_name}):
return FindPrograms(app_name=app_name)
case ExitSuccess(
value={"command": "npm", "app_name": app_name, "args": args}
):
Expand Down
33 changes: 20 additions & 13 deletions src/djelm/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
Create = TypedDict("Create", {"command": Literal["create"], "app_name": str})
List = TypedDict("List", {"command": Literal["list"]})
ListWidgets = TypedDict("ListWidgets", {"command": Literal["listwidgets"]})
FindPrograms = TypedDict(
"FindPrograms", {"command": Literal["findprograms"], "app_name": str}
)
AddProgram = TypedDict(
"AddProgram",
{"command": Literal["addprogram"], "app_name": str, "program_name": str},
Expand Down Expand Up @@ -63,6 +66,7 @@ def acceptable_command(
Create
| List
| ListWidgets
| FindPrograms
| AddProgram
| Npm
| Elm
Expand Down Expand Up @@ -96,8 +100,6 @@ def __check_existing(self, xs: list[str]) -> None:
Validations.__not_a_djelm_app("create", app_name)
)
case ["npm", app_name, *_]:
app_path_exit = get_app_path(app_name)

validated_app_path = self._validate_app_path(
app_name, self.__not_in_settings("npm", app_name)
)
Expand All @@ -107,8 +109,6 @@ def __check_existing(self, xs: list[str]) -> None:
Validations.__not_a_djelm_app("npm", app_name)
)
case ["elm", app_name, *_]:
app_path_exit = get_app_path(app_name)

validated_app_path = self._validate_app_path(
app_name, self.__not_in_settings("elm", app_name)
)
Expand All @@ -117,24 +117,18 @@ def __check_existing(self, xs: list[str]) -> None:
Validations.__not_a_djelm_app("elm", app_name)
)
case ["watch", app_name]:
app_path_exit = get_app_path(app_name)

validated_app_path = self._validate_app_path(
app_name, self.__not_in_settings("watch", app_name)
)
if not is_djelm(next(walk_level(validated_app_path))[2]):
raise ValidationError(self.__not_a_djelm_app("watch", app_name))
case ["compile", app_name]:
app_path_exit = get_app_path(app_name)

validated_app_path = self._validate_app_path(
app_name, self.__not_in_settings("compile", app_name)
)
if not is_djelm(next(walk_level(validated_app_path))[2]):
raise ValidationError(self.__not_a_djelm_app("compile", app_name))
case ["compilebuild", app_name]:
app_path_exit = get_app_path(app_name)

validated_app_path = self._validate_app_path(
app_name, self.__not_in_settings("compilebuild", app_name)
)
Expand All @@ -160,8 +154,6 @@ def __check_existing(self, xs: list[str]) -> None:
)

case ["addprogram", app_name, _]:
app_path_exit = get_app_path(app_name)

validated_app_path = self._validate_app_path(
app_name, self.__not_in_settings("addprogram", app_name)
)
Expand All @@ -182,9 +174,16 @@ def __check_existing(self, xs: list[str]) -> None:
)}
\033[4m\033[1mHint\033[0m: These files are usually automatically generated for you when you run the \033[1mcreate\033[0m commands."""
)
case ["findprograms", app_name]:
validated_app_path = self._validate_app_path(
app_name, self.__not_in_settings("addprogram", app_name)
)
if not is_djelm(next(walk_level(validated_app_path))[2]):
raise ValidationError(
f'{Validations.__not_a_djelm_app("findprograms", app_name)}\n'
)

case ["generatemodel", app_name, p]:
app_path_exit = get_app_path(app_name)
namespace = to_program_namespace(p.split("."))
namespace_path, prog_name = namespace

Expand Down Expand Up @@ -238,6 +237,8 @@ def __check_command_combos(self, xs: list[str]) -> None:
return
case ["listwidgets"]:
return
case ["findprograms", _]:
return
case ["compile", _]:
return
case ["compilebuild", _]:
Expand Down Expand Up @@ -278,6 +279,8 @@ def __check_command_combos(self, xs: list[str]) -> None:
"elm",
]:
raise ValidationError(Validations.__missing_app_name("elm"))
case ["findprograms"]:
raise ValidationError(Validations.__missing_app_name("findprograms"))
case ["npm", _, *_]:
return
case ["elm", _, *_]:
Expand Down Expand Up @@ -493,6 +496,7 @@ def __check_command_verb(s: str) -> None:
"elm",
"list",
"listwidgets",
"findprograms",
"addprogram",
"addwidget",
"watch",
Expand All @@ -510,6 +514,7 @@ def __command_exit(
Create
| List
| ListWidgets
| FindPrograms
| AddProgram
| Npm
| Elm
Expand Down Expand Up @@ -555,6 +560,8 @@ def __command_exit(
return ExitSuccess(
{"command": "addwidget", "app_name": v, "widget": widget_name}
)
case ["findprograms", v]:
return ExitSuccess({"command": "findprograms", "app_name": v})
case _ as cmds:
return ExitFailure(
cmds,
Expand Down
4 changes: 2 additions & 2 deletions tests/test_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,13 @@ class Meta:
return UserForm


def test_fuzz_flags():
def test_program_fuzzer():
app_name = "test_programs"
src_path = get_app_src_path(app_name).value # type:ignore

programs = []

for i in range(1, 13):
for i in range(1, 15):
flags = fuzz_flag()

class MockHandler(ModelGenerator):
Expand Down
Loading

0 comments on commit 7701673

Please sign in to comment.