Skip to content
Merged
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
2 changes: 1 addition & 1 deletion lib/i18n/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
CLI_APP = "when"
UI_APP_LABEL = "When Automation Tool"
UI_APP_COPYRIGHT = "© 2023-2026 Francesco Garosi"
UI_APP_VERSION = "2.1.1"
UI_APP_VERSION = "2.1.2"

# other strings that should not be translated
UI_WHENEVER = "whenever"
Expand Down
61 changes: 43 additions & 18 deletions lib/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
string,
items,
exceptions,
)
)
from hashlib import blake2s
from base64 import decodebytes as b64_decodeb
from io import BytesIO
Expand Down Expand Up @@ -85,13 +85,24 @@

# operators allowed in WMI and DBus result/parameter checks
_WMI_RESULT_CHECK_OPERATORS = ("eq", "neq", "gt", "ge", "lt", "le", "match")
_DBUS_PARAM_CHECK_OPERATORS = ("eq", "neq", "gt", "ge", "lt", "le", "match", "contains", "ncontains")
_DBUS_PARAM_CHECK_OPERATORS = (
"eq",
"neq",
"gt",
"ge",
"lt",
"le",
"match",
"contains",
"ncontains",
)


# check that an operator is correct for either DBus or WMI result checks
def is_wmi_operator(s: str) -> bool:
return s in _WMI_RESULT_CHECK_OPERATORS


def is_dbus_operator(s: str) -> bool:
return s in _DBUS_PARAM_CHECK_OPERATORS

Expand Down Expand Up @@ -198,18 +209,21 @@ def get_appicon(image: bytes) -> ImageTk.PhotoImage:
# determine where configuration is stored by default
def get_default_configdir() -> str:
if is_windows():
appdata = os.environ["APPDATA"]
cfgname: str = AppConfig.get("CFGNAME") # type: ignore
appdata = os.getenv("APPDATA") or os.path.join(
os.getenv("USERPROFILE"), # type: ignore
"AppData",
)
cfgname: str = AppConfig.get("CFGNAME") # type: ignore
if AppConfig.get("DEBUG"):
cfgname += "_DEBUG"
return os.path.join(appdata, cfgname)
else:
s: str = AppConfig.get("CFGNAME") # type: ignore
s: str = AppConfig.get("CFGNAME") # type: ignore
cfgname = "." + s.lower()
if AppConfig.get("DEBUG"):
cfgname += "_DEBUG"
home = os.path.expanduser("~")
if sys.platform == "darwin":
if is_mac():
return os.path.join(home, "Library", "Application Support", cfgname)
elif is_linux():
return os.path.join(home, cfgname)
Expand Down Expand Up @@ -238,18 +252,27 @@ def get_default_whenever() -> str | None:

# determine appdata directory and ensure it exists
def get_appdata() -> str:
appdata: str = AppConfig.get("APPDATA") # type: ignore
appdata: str = AppConfig.get("APPDATA") # type: ignore
if not os.path.isdir(appdata):
try:
os.makedirs(appdata)
except Exception:
raise OSError(CLI_ERR_DATADIR_UNACCESSIBLE)
# the following is to work around UWP redirection on Windows: not sure
# whether or not it can be of any use on other platforms, so this remains
# Windows specific for the moment
if is_windows():
realappdata = os.path.realpath(appdata)
if realappdata != appdata:
appdata = realappdata
AppConfig.delete("APPDATA")
AppConfig.set("APPDATA", appdata)
return appdata


# determine scripts directory and ensure that it exists
def get_scriptsdir() -> str:
configdir: str = AppConfig.get("APPDATA") # type: ignore
configdir: str = AppConfig.get("APPDATA") # type: ignore
if is_windows():
subdir = "Scripts"
else:
Expand All @@ -264,8 +287,8 @@ def get_scriptsdir() -> str:


# determine temp directory and ensure that it exists
def get_tempdir(cleanup: bool=False) -> str:
configdir: str = AppConfig.get("APPDATA") # type: ignore
def get_tempdir(cleanup: bool = False) -> str:
configdir: str = AppConfig.get("APPDATA") # type: ignore
if is_windows():
subdir = "Temp"
else:
Expand Down Expand Up @@ -345,7 +368,7 @@ def get_lua_path() -> str:
def get_lua_initscript() -> str:
init = os.path.join(get_scriptsdir(), _LUA_INIT_NAME)
if not os.path.exists(init):
with open(init, 'w') as f:
with open(init, "w") as f:
f.write(_LUA_INIT_SCRIPT)
return init

Expand Down Expand Up @@ -484,28 +507,31 @@ def is_whenever_running() -> None | bool:
def whenever_has_dbus() -> bool:
return bool(AppConfig.get("WHENEVER_HAS_DBUS"))


def whenever_has_wmi() -> bool:
return bool(AppConfig.get("WHENEVER_HAS_WMI"))


def whenever_has_lua_sync() -> bool:
return bool(AppConfig.get("WHENEVER_HAS_LUASYNC"))


def whenever_has_lua_httpreq() -> bool:
return bool(AppConfig.get("WHENEVER_HAS_LUAHTTPREQ"))


# return the configuration file path
def get_configfile() -> str:
s: str = AppConfig.get("CFGNAME") # type: ignore
d: str = AppConfig.get("APPDATA") # type: ignore
s: str = AppConfig.get("CFGNAME") # type: ignore
d: str = AppConfig.get("APPDATA") # type: ignore
basename = "%s.toml" % s.lower()
return os.path.join(d, basename)


# return the log file path
def get_logfile() -> str:
s: str = AppConfig.get("CFGNAME") # type: ignore
d: str = AppConfig.get("APPDATA") # type: ignore
s: str = AppConfig.get("CFGNAME") # type: ignore
d: str = AppConfig.get("APPDATA") # type: ignore
basename = "%s.log" % s.lower()
return os.path.join(d, basename)

Expand Down Expand Up @@ -619,9 +645,9 @@ def toml_list_of_literals(los) -> items.Array | None:
# in the array, possibly followed by the first non-dashed argument
def toml_list_of_command_args(los) -> items.Array | None:
if los is not None:
switch_start = ['-', '--']
switch_start = ["-", "--"]
if is_windows():
switch_start.append('/')
switch_start.append("/")
cur_line = []
r = array()
for s in los:
Expand Down Expand Up @@ -649,7 +675,6 @@ def toml_literal(s) -> items.String | None:
return toml_try_literal(s)



# clean a caption from non-alphanumeric characters at the end
def clean_caption(s) -> str:
s = " ".join(s.split())
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "when"
version = "2.1.1"
version = "2.1.2"
description = "Interface for the **whenever** automation tool"
authors = [
{ name = "Francesco Garosi", email = "francesco.garosi@gmail.com" },
Expand Down Expand Up @@ -32,7 +32,7 @@ Pygments = "^2.19.2"
pygobject = { version = "3.50.0", markers = "sys_platform == 'linux'" }
# pygobject = { version = "^3.52.2", markers = "sys_platform == 'linux'" }
pystray = "^0.19.5"
pywin32 = { version = "^308", markers = "sys_platform == 'win32'" }
pywin32 = { version = ">=308", markers = "sys_platform == 'win32'" }
requests = "^2.32.5"
rich = "^13.9.4"
semver = "^3.0.4"
Expand Down
8 changes: 6 additions & 2 deletions support/docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ Note that even though a [recent release](https://github.com/almostearthling/when

These steps can be followed on both Windows 10 and Windows 11:

1. install **Python** using the standalone installer, and choosing to install it for all users[^1]
1. install **Python**, preferably using one of the following methods:[^1]
* the [_winget_](https://learn.microsoft.com/en-us/windows/package-manager/winget) utility, by running `winget install Python` in a terminal window[^2]
* the [_Python Install Manager_](https://apps.microsoft.com/detail/9nq7512cxl7t), by running `py install 3` in a terminal window after installing it from the store[^2]
* the standalone installer, which can be found at the downloads page on the Python main site
2. install pipx by issuing the command `py -m pip install --user pipx` in a console window: after installation launch `pipx ensurepath` from the command prompt
3. install the latest release of **When**, using **pipx**:

Expand Down Expand Up @@ -164,4 +167,5 @@ Both the installation of **When** using the **pipx** method, and the installatio
[`◀ Main`](main.md)


[^1]: the reason is that, when Python is installed for the current user or via the _App Store_, the APPDATA directory is relocated within the interpreter to a non-canonical location; the problem is under investigation in order to support all installation methods for Python.
[^1]: the reason is that, when Python is installed via the _Microsoft Store_, the _APPDATA_ directory is relocated within the interpreter in order to confine it in a sandbox: the way of handling file system redirections for packaged applications is [documented here](https://learn.microsoft.com/en-us/windows/msix/desktop/desktop-to-uwp-behind-the-scenes#file-system). **When** uses the redirected location to store configuration and data, and **whenever** works anyway, however this type of setup is discouraged because it leads to configuration files that are both hard to read and hard to find. Other installations performed using either _winget_, or the _Python Install Manager_, which in turn is becoming the preferred installation method for Python on Windows, or the standalone Python installer, are not affected by this problem. If a _Microsoft Store_ based setup is really needed, be careful to disable the setting that limits the path length, as it might cause issues because of the intrinsic length of the base path.
[^2]: the _PATH_ variable might need to be adjusted if this method is used, as suggested by the `pip` command, or the command `python -m pipx` has to be used instead of simply typing `pipx`; the same yields for _Microsoft Store_ based Python installations.
2 changes: 1 addition & 1 deletion support/docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The examples assume that **When** has been installed using the [suggested method
It's sometimes useful to have the ability to trace the configuration of one or more conditions for debug purposes: configuring a condition can sometimes be awful, especially when it is one of the most complex ones (such as a [command](cond_actionrelated.md#command) or a [Lua](cond_actionrelated.md#lua-script) based condition), and you might just want to test your condition with no side effects other than dropping a line to the log. For this I use a simple _trace_ task, consisting in a minimal Lua script:

```lua
log.warn("Trace: *** VERIFIED CONDITION *** `" .. whenever_condition .. "`");
log.warn("Trace: *** VERIFIED CONDITION *** `" .. whenever_condition .. "`")
```

that exploits the [abilities](lua_considerations.md) of the internal Lua interpreter to
Expand Down
Loading