Test Errors, if `getpass.getuser` fails, i.e. tox on windows

65 views
Skip to first unread message

Glen Fletcher

unread,
Mar 20, 2023, 11:08:45 AM3/20/23
to Django developers (Contributions to Django itself)
There are 4 tests in `auth_test/test_management.py` which fail when run by tox on windows.

- `test_system_username` - Raises an `ImportError`
- `test_non_ascii_verbose_name` - mock input mapping assumes default username, and raise an error
- `test_default_username` - Enters an infinite loop asking for a valid username
- `test_createsuperuser_command_suggested_username_with_database_option` - Enters an infinite loop asking for a
  valid username

I've traced this to `getpass.getuser`. Under windows `import pwd` cause an `ImportError`, unless `USERNAME` is set,
tox doesn't seem to  include this environment variable in the environments it creates, even when I tried adding
`USERNAME` to `passenv` in `tox.ini:testenv` (Note this is the first time I've used tox, so I could be doing
something wrong).

This causes `test_system_username` to raise an `ImportError` thereby failing and `get_system_username` to return an
empty string.

This means that `get_default_username` also returns an empty string. This causes `test_non_ascii_verbose_name` to
have an invalid mock input mapping as its map assumes the username request has a default option.

Further the empty string from `get_default_username`, results in a input validation error causing `get_input_data`
to return `None` resulting in `test_default_username` and
`test_createsuperuser_command_suggested_username_with_database_option` causing them to  enter an infinite loop
requesting a valid username, in `handle`.

See Code extracts below:

**def handle, django/contrib/auth/management/commands/createsuperuser.py:90**
*(username loop, starting on line 122)*
```python
while username is None:
    message = self._get_input_message(
        self.username_field, default_username
    )
    username = self.get_input_data(
        self.username_field, message, default_username
    )
    if username:
        error_msg = self._validate_username(
            username, verbose_field_name, database
        )
        if error_msg:
            self.stderr.write(error_msg)
            username = None
            continue
```

**def get_input_data, django/contrib/auth/management/commands/createsuperuser.py:250**
```python
def get_input_data(self, field, message, default=None):
    """
    Override this method if you want to customize data inputs or
    validation exceptions.
    """
    raw_value = input(message)
    if default and raw_value == "":
        raw_value = default
    try:
        val = field.clean(raw_value, None)
    except exceptions.ValidationError as e:
        self.stderr.write("Error: %s" % "; ".join(e.messages))
        val = None

    return val
```

 **def get_system_username, django/contrib/auth/management/__init__.py:114**
```python
def get_system_username():
    """
    Return the current system user's username, or an empty string if the
    username could not be determined.
    """
    try:
        result = getpass.getuser()
    except (ImportError, KeyError):
        # KeyError will be raised by os.getpwuid() (called by getuser())
        # if there is no corresponding entry in the /etc/passwd file
        # (a very restricted chroot environment, for example).
        return ""
    return result
```

**def getuser, getpass.py:154**
```python
def getuser():
    """Get the username from the environment or password database.

    First try various environment variables, then the password
    database.  This works on Windows as long as USERNAME is set.

    """

    for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'):
        user = os.environ.get(name)
        if user:
            return user

    # If this fails, the exception will "explain" why
    import pwd
    return pwd.getpwuid(os.getuid())[0]
```

## TOX Environment
Below I've include a sanitized version of my tox environment, generated by the Python Debug console (pycharm python
debugger)

```pycon
for k, v in os.environ.items():
    print(f"{k}: {v}")

PROGRAMFILES: C:\Program Files
TEMP: c:\users\{username}\AppData\Local\Temp
SYSTEMDRIVE: C:
PROGRAMFILES(X86): C:\Program Files (x86)
PATHEXT: .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW;.RB;.RBW
TMP: c:\users\{username}\AppData\Local\Temp
USERPROFILE: c:\users\{username}
COMSPEC: C:\WINDOWS\system32\cmd.exe
PATH: {repo_path}\.tox\py3\Scripts;{venv_path};{python_path};{windows_path}
SYSTEMROOT: C:\WINDOWS
PROGRAMDATA: C:\ProgramData
APPDATA: c:\users\{username}\AppData\Roaming
PROCESSOR_ARCHITECTURE: AMD64
NUMBER_OF_PROCESSORS: 20
PYTHONDONTWRITEBYTECODE: 1
PYTHONHASHSEED: 261
TOX_ENV_NAME: py3
TOX_ENV_DIR: {repo_path}\.tox\py3
_JB_DO_NOT_CALL_ENTER_MATRIX: 1
VIRTUAL_ENV: {repo_path}\repo\.tox\py3
TMPDIR: c:\users\{username}\AppData\Local\Temp\django_0vhaflop
DJANGO_SETTINGS_MODULE: test_sqlite
RUNNING_DJANGOS_TEST_SUITE: true
```

## Possible Solution

I was able to solve this by adding `os.environ.setdefault("USERNAME", "test_user")` to `runtests.py` to ensure that
`USERNAME` is always defined so `getpass.getuser` will just return that value. However I'm not sure this is the
correct solution.

## Note
I'm using tox installed in my virtual environment using the command `pip install 'tox<4'` as when using
the default tox install (version 4) it failed to install django as an editable package in the tox environment (in
the config it was listing `PACKAGE=skip` I'm not sure why this happened)
Reply all
Reply to author
Forward
0 new messages