diff --git a/did/base.py b/did/base.py index e56a1d84..d50ee59a 100644 --- a/did/base.py +++ b/did/base.py @@ -409,6 +409,12 @@ class User(): some@email.org; bz: bugzilla@email.org; gh: githublogin + By default, the alias type is autodetected based on the presence of + "@" character (email if present, login otherwise). However, you can + explicitly specify the alias type using the extended syntax:: + + some@email.org; jira.login: user@example.com; gh.email: user + Use config section name to identify stats where given alias should be used. The exactly same syntax can be used both in the config file and on the command line. Finally it's also possible to include the @@ -469,16 +475,47 @@ def alias(self, aliases, stats): # Check for aliases specified in the email string if aliases is not None: try: - aliases = dict([ - re.split(r"\s*:\s*", definition, maxsplit=1) - for definition in re.split(r"\s*;\s*", aliases.strip())]) + explicit_aliases = {} # For stats.type: value format + implicit_aliases = {} # For stats: value format + + for definition in re.split(r"\s*;\s*", aliases.strip()): + parts = re.split(r"\s*:\s*", definition, 1) + if len(parts) != 2: + raise ValueError(f"Invalid alias format: '{definition}'") + + key, value = parts + + # Check if key contains type specification + # (e.g., "jira.login") + if "." in key: + stats_name, alias_type = key.split(".", 1) + if alias_type not in ("email", "login"): + raise ValueError(f"Invalid alias type '{alias_type}', " + "must be 'email' or 'login'") + if stats_name not in explicit_aliases: + explicit_aliases[stats_name] = {} + explicit_aliases[stats_name][alias_type] = value + else: + # Standard format: stats: value + # (autodetect type) + implicit_aliases[key] = value + + # Apply explicit aliases first + if stats in explicit_aliases: + if "email" in explicit_aliases[stats]: + email = explicit_aliases[stats]["email"] + if "login" in explicit_aliases[stats]: + login = explicit_aliases[stats]["login"] + # Then apply implicit aliases + # (only if no explicit alias found) + elif stats in implicit_aliases: + if "@" in implicit_aliases[stats]: + email = implicit_aliases[stats] + else: + login = implicit_aliases[stats] + except ValueError as exc: raise ConfigError(f"Invalid alias definition: '{aliases}'") from exc - if stats in aliases: - if "@" in aliases[stats]: - email = aliases[stats] - else: - login = aliases[stats] # Update login/email if alias detected if email is not None: self.email = email diff --git a/tests/test_base.py b/tests/test_base.py index 5f83a020..4863092b 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -260,6 +260,53 @@ def test_user_class(): clone = user.clone("bz") assert clone.login == "bzlogin" + # Explicit email alias type + user = did.base.User("some@email.org; jira.email: jirauser", stats="jira") + assert user.email == "jirauser" + assert user.login == "jirauser" # should update login from email + + # Explicit login alias type + user = did.base.User("some@email.org; jira.login: user@example.com", stats="jira") + assert user.login == "user@example.com" + assert user.email == "some@email.org" # original email unchanged + + # Invalid alias type + with pytest.raises(did.base.ConfigError, match="Invalid alias definition"): + did.base.User("some@email.org; jira.invalid: value", stats="jira") + + # Invalid alias format (missing colon) + with pytest.raises(did.base.ConfigError, match="Invalid alias definition"): + did.base.User("some@email.org; jira.login value", stats="jira") + + # Case sensitive alias types - uppercase should fail + with pytest.raises(did.base.ConfigError, match="Invalid alias definition"): + did.base.User("some@email.org; jira.EMAIL: jirauser", stats="jira") + + with pytest.raises(did.base.ConfigError, match="Invalid alias definition"): + did.base.User("some@email.org; jira.LOGIN: user@example.com", stats="jira") + + # Both login and email aliases for the same stats service on same + # line When both are defined, both should be applied + user = did.base.User( + "some@email.org; jira.login: jirauser; jira.email: jira@example.com", + stats="jira") + assert user.login == "jirauser" + assert user.email == "jira@example.com" + + # Test different order - email first, then login + user = did.base.User( + "some@email.org; jira.email: jira@example.com; jira.login: jirauser", + stats="jira") + assert user.login == "jirauser" + assert user.email == "jira@example.com" + + # Explicit aliases take precedence over implicit ones + user = did.base.User( + "some@email.org; jira: implicit_value; jira.login: explicit_login", + stats="jira") + assert user.login == "explicit_login" + assert user.email == "some@email.org" # original email unchanged + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Exceptions