'Azure pipeline can't find the user created python module

I am attempting to run tests from a python module using Azure DevOps. I've got a pipeline build set up to build of a yml file and also using classic editor. I'm getting an error that my module name on my imports is not right. When I run this locally, it works just fine.

My Repo structure:

Here is my repo structure

I'm running my test using this command as a batch file:

cd testcases

pytest -v test_msoffice.py

and gives me error:

______________________ ERROR collecting test_msoffice.py ______________________
ImportError while importing test module 'D:\a\1\s\testcases\test_msoffice.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
test_msoffice.py:7: in <module>
    from util import utility_method_class
E   ModuleNotFoundError: No module named util

Someone please help me.



Solution 1:[1]

Pretty sure this is related to Azure DevOps Pipelines - Python - ModuleNotFoundError despite sys.path.append() or setting PYTHONPATH, where it says:

After talking to the Azure DevOps Support, I now know that my issue is a bug within DevOps-Pipelines at the moment.

Solution 2:[2]

I ran into the same symptoms over the last two days. I assume locally your package is in a directory called "util", with (or as per your comment without) an __init__.py file. This leads python to recognise all modules under that directory as belonging to the utils package. (From Python 3.something python will manage "namespace references" to the local directory structure without __init__.py) On the Pipeline agent the code is directly copied into D:\a\1\s\ - therefore python will consider the package to be called "s"

I found the following workaround: include a script in the pipeline to move the source code into a new suitably named directory and then test it there.

My Pipeline YAML looks like this. It is based on the assumption that your ADO project is named the same as your package - so in your case "util" and is set up for ubuntu / bash. If you must test on windows agents then the scripting will need porting, or you can switch to ubuntu agents and copy the whole thing.

# Python package
# Create and test a Python package on multiple Python versions.
# Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/python

trigger:
- azure-pipelines
- master
- pipeline/*

variables:
  PythonPackagePath: ORIGINALVALUE

pool:
  vmImage: ubuntu-latest
strategy:
  matrix:
    # Python27:
    #   python.version: '2.7'
    # Python35:
    #  python.version: '3.5'
    # Python36:
    #   python.version: '3.6'
    # Python37:
    #   python.version: '3.7'
    Python310:
      python.version: '3.10'

steps:
- task: UsePythonVersion@0
  inputs:
    versionSpec: '$(python.version)'
  displayName: 'Use Python $(python.version)'

- script: |
    cd $(System.DefaultWorkingDirectory)
    export ParentDir=${PWD%/*}

    echo Setting PythonPackagePath to $ParentDir/$(System.TeamProject)
    echo "##vso[task.setvariable variable=PythonPackagePath;]$ParentDir/$(System.TeamProject)"

  displayName: 'Set Package Path'

- script: |
    echo PythonPackagePath is $(PythonPackagePath)
    
    echo Moving files from $(System.DefaultWorkingDirectory) to $(PythonPackagePath)
    mv $(System.DefaultWorkingDirectory) $(PythonPackagePath)

    echo SymLinking old Working Directory back in case needed
    ln -s $(PythonPackagePath) $(System.DefaultWorkingDirectory)
    ls -l $(System.DefaultWorkingDirectory)/..
  displayName: 'Move Working Directory'

- script: |
    python -m pip install --upgrade pip
    pip install -r requirements.txt
  displayName: 'Install dependencies'
  # continueOnError: true

- script: |
    export PYTHONPATH=$(PythonPackagePath)
    echo PYTHONPATH is set to $PYTHONPATH

    cd $(PythonPackagePath)
    echo Working in:
    pwd

    pip install pytest pytest-azurepipelines
    pip install pytest-cov
    pytest --cov=. --cov-report=xml
  displayName: 'pytest'

- task: PublishCodeCoverageResults@1
  inputs:
    codeCoverageTool: Cobertura
    summaryFileLocation: '$(PythonPackagePath)/**/coverage.xml'

A few traps I fell into along the way:

  1. ADO pipeline variables of the form $(VariableName) are resolved when the step starts, not when the line is reached - so you need to set the variable in a previous step before using it.
  2. bash will keep a relative directory reference in place in a variable. So you cannot export ParentDir=.. nor export ParentDir=$(System.DefaultWorkingDirectory)/.. in the first case ParentDir is always the parent of the current working directory, in the second the symlinking will fail due to circular references. The RegEx ${PWD%/*} gets the value of the current directory and returns the value up to the last slash.
  3. pytest does not like following symlinks - so you cannot simply ln -s path/to/code/s path/to/code/PackageName pytest will still consider the package to be called s
  4. Setting PYTHONPATH is not strictly necessary, running pytest from the right directory seems to be the most stable option in thie configuration
  5. I had a few issues with getting code coverage to publish properly. I think it also doesn't like symlinks - so don't forget to update the path in the relevant step as well.

This works regardless of whether tests are in a subfolder. My overall structure is:

FooBar
      __init__.py
    ¦
    -- Doomsday
      __init__.py
      Fuel.py
      test_Fuel.py
      ...
    ¦
    -- Utils
          __init__.py
          Maths.py
          ...
        ¦
        --test
              test_Maths1.py
              test_Maths2.py
              ...

where:

  • FooBar is also the name of my project in ADO
  • pytest tests for Doomsday are in the Doomsday directory
  • pytest tests for Utils are in a subfolder
  • references are in the form from FooBar.Utils.Maths import prod and these work correctly

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 laike9m
Solution 2 MusicalNinja