Allow configuration of multiple paths - #615
Conversation
This introduces a new configuration variable called "paths" which can be set to a list to include multiple directories of directories with .ics files. This is useful when, for example, different directories are used for private and work-related to-do entries, which cannot be easily matched using a single glob(3) expression. The proposed patch is backwards compatible with the previous configuration format as the list's value defaults to a single-element list containing the value of the previous "path" configuration option, if the "paths" option is unset.
d89d49b to
a3a7fe5
Compare
|
Not a Python person myself, but I assume the mypy error on the CI are not caused by my changes? mypy error output on the build.sr.ht/alpine-py312 CI.lint: commands[2]> mypy . todoman/widgets.py:70: error: No return value expected [return-value] todoman/widgets.py:70: error: Argument 1 to "keypress" of "Edit" has incompatible type "tuple[int, int] | tuple[int] | tuple[()]"; expected "tuple[int]" [arg-type] todoman/widgets.py:123: error: "Widget" has no attribute "_loop" [attr-defined] todoman/widgets.py:150: error: Cannot infer value of type parameter "WrappedWidget" of "__init__" of "WidgetWrap" [misc] todoman/interactive.py:59: error: Incompatible types in assignment (expression has type "Text", variable has type "str") [assignment] todoman/interactive.py:60: error: List item 0 has incompatible type "tuple[int, str]"; expected "Widget | tuple[Literal['pack', WHSettings.PACK] | int, Widget] | tuple[Literal['given', WHSettings.GIVEN], int, Widget] | tuple[Literal['weight', WHSettings.WEIGHT], int | float, Widget]" [list-item] todoman/interactive.py:60: error: List item 1 has incompatible type "object"; expected "Widget | tuple[Literal['pack', WHSettings.PACK] | int, Widget] | tuple[Literal['given', WHSettings.GIVEN], int, Widget] | tuple[Literal['weight', WHSettings.WEIGHT], int | float, Widget]" [list-item] todoman/interactive.py:63: error: Argument 1 to "Pile" has incompatible type "list[tuple[str, Columns]]"; expected "Iterable[Widget | tuple[Literal['pack', WHSettings.PACK] | int, Widget] | tuple[Literal['given', WHSettings.GIVEN], int, Widget] | tuple[Literal['weight', WHSettings.WEIGHT], int | float, Widget]]" [arg-type] todoman/interactive.py:77: error: Argument "parent" to "ExtendedEdit" has incompatible type "TodoEditor"; expected "Widget" [arg-type] todoman/interactive.py:81: error: Argument "parent" to "ExtendedEdit" has incompatible type "TodoEditor"; expected "Widget" [arg-type] todoman/interactive.py:86: error: Argument "parent" to "ExtendedEdit" has incompatible type "TodoEditor"; expected "Widget" [arg-type] todoman/interactive.py:90: error: Argument "parent" to "ExtendedEdit" has incompatible type "TodoEditor"; expected "Widget" [arg-type] todoman/interactive.py:94: error: Argument "parent" to "ExtendedEdit" has incompatible type "TodoEditor"; expected "Widget" [arg-type] todoman/interactive.py:98: error: Argument "parent" to "ExtendedEdit" has incompatible type "TodoEditor"; expected "Widget" [arg-type] todoman/interactive.py:103: error: Argument "parent" to "PrioritySelector" has incompatible type "TodoEditor"; expected "Widget" [arg-type] todoman/interactive.py:143: error: "ListWalker" has no attribute "contents" [attr-defined] todoman/interactive.py:144: error: "ListWalker" has no attribute "contents" [attr-defined] todoman/interactive.py:146: error: "ListWalker" has no attribute "contents" [attr-defined] todoman/interactive.py:156: error: Incompatible types in assignment (expression has type "MainLoop", variable has type "None") [assignment] todoman/interactive.py:159: error: Argument "unhandled_input" to "MainLoop" has incompatible type "Callable[[str], None]"; expected "Callable[[str | tuple[str, int, int, int]], bool | None] | None" [arg-type] tests/test_widgets.py:16: error: Argument 1 to "ExtendedEdit" has incompatible type "None"; expected "Widget" [arg-type] tests/test_widgets.py:44: error: Argument 1 to "ExtendedEdit" has incompatible type "None"; expected "Widget" [arg-type] tests/test_widgets.py:60: error: Argument 1 to "ExtendedEdit" has incompatible type "None"; expected "Widget" [arg-type] tests/test_widgets.py:82: error: Argument 1 to "ExtendedEdit" has incompatible type "None"; expected "Widget" [arg-type] tests/test_widgets.py:98: error: Argument 1 to "ExtendedEdit" has incompatible type "None"; expected "Widget" [arg-type] tests/test_widgets.py:114: error: Argument 1 to "ExtendedEdit" has incompatible type "None"; expected "Widget" [arg-type] tests/test_widgets.py:168: error: Argument 1 to "PrioritySelector" has incompatible type "None"; expected "Widget" [arg-type] tests/test_ui.py:120: error: "ListWalker" has no attribute "contents" [attr-defined] tests/test_ui.py:124: error: "ListWalker" has no attribute "contents" [attr-defined] tests/test_ui.py:155: error: "ListWalker" has no attribute "contents" [attr-defined] Found 30 errors in 4 files (checked 24 source files) With respect to the CI coverage failure, would address this by adding tests if we agree on this API (see the original description for possible alternative designs). |
WhyNotHugo
left a comment
There was a problem hiding this comment.
Overall idea sounds good. Note a huge fan, but I don't see a cleaner way to support this, and the request sounds reasonable.
I think we should fail with an error when both path and paths are set.
Mostly minor comments, general changes are good.
| paths = [ | ||
| path | ||
| for path in glob.iglob(path) | ||
| if isdir(path) and not path.endswith("__pycache__") |
There was a problem hiding this comment.
Ignoring __pycache__ specifically is unusual, but acceptable I guess.
There was a problem hiding this comment.
That was part of the existing implementation, hence I retained it.
Line 366 in 78e3578
| paths = ctx.config["paths"] | ||
| if len(paths) == 0: | ||
| raise exceptions.NoListsFoundError(ctx.config["path"]) | ||
| paths = glob_path(ctx.config["path"]) |
There was a problem hiding this comment.
Shouldn't we update ctx.config["paths"] with this new value?
There was a problem hiding this comment.
Is there a specific reason why this should be necessary? The existing implementation does also not update ctx.config["path"] after calling .iglob() on it:
Lines 363 to 369 in 78e3578
a3a7fe5 to
91c21b3
Compare
Thanks for the feedback! This could get a little cleaner by deprecating the |
91c21b3 to
89949e6
Compare
| # XXX: There is no good way to check if an option has been explicitly set. | ||
| default_path = expand_path(CONFIG_SPEC[0].default) | ||
| if ctx.config["path"] != default_path and len(ctx.config["paths"]) != 0: | ||
| raise ConfigurationError("Both 'path' and 'paths' is set, use one.") | ||
|
|
There was a problem hiding this comment.
I think we should fail with an error when both
pathandpathsare set.
Implemented this here, but it's a little hacky as path has a default value and we thus need to determine if it has been explicitly set by the user, for which there doesn't seem to be an API at the moment.
There was a problem hiding this comment.
Also can't do this via a configuration validation function as it doesn't have access to the already parsed options.
89949e6 to
79e3cf8
Compare
|
As an alternative, potentially less hacky design, we could also make |
For my work machine, I want to be able to access both my private and work-related to-dos. Unfortunately, they are stored in two entirely different directories, which I cannot easily match using a single
glob(3)expression. For this kind of use case, this PR introduces a new configuration variable calledpathswhich can be set to a list ofglob(3)expressions to include multiple directories with.icsfiles.The proposed patch is backwards compatible with the previous configuration format as the list's value defaults to a single-element list containing the value of the previous
pathconfiguration option, if thepathsoption is unset.I intentionally tried to implement this in a way that retains backwards compatibility, I would also be open to other designs. For example, making the existing
pathconfiguration option a list of strings instead of a string. I haven't added tests yet but can do so once we agreed on a design.