Search code examples
pythondockertravis-ci

AttributeError: 'ParsedRequirement' object has no attribute 'req'


I have docker file with one layer as

RUN python setup.py develop

I am using a mutli-stage build with three stages and this is the stage one all the stages have the same base image, though I don't think this is a problem with dockerfile but seems to be a problem with python and the way it is executed working on the base image python:3.7-slim I am building this dockerfile on Travis CI with this below Version info on Travis:

docker version
Client:
 Version:      17.09.0-ce
 API version:  1.32
 Go version:   go1.8.3
 Git commit:   afdb6d4
 Built:        Tue Sep 26 22:42:38 2017
 OS/Arch:      linux/amd64
Server:
 Version:      17.09.0-ce
 API version:  1.32 (minimum version 1.12)
 Go version:   go1.8.3
 Git commit:   afdb6d4
 Built:        Tue Sep 26 22:41:20 2017
 OS/Arch:      linux/amd64
 Experimental: false

I get this below error as

AttributeError: 'ParsedRequirement' object has no attribute 'req'

Surprisingly I am able to have this working on My mac machine with docker version 19.03.2 Here is my setup.py file

import os
import shutil
import inspect
import platform
from setuptools import setup
import setuptools
try:
    from pip.req import parse_requirements
except ImportError:
    from pip._internal.req import parse_requirements

EMAIL_CONF = 'email.conf'
DL_CONF = 'dl.conf'
LINUX_CONFDIR = os.path.expanduser('~') + '/.config/bassa/'
WIN_CONFDIR = os.path.expanduser('~') + '/%app_data%/bassa/'
OSX_CONFDIR  = os.path.expanduser('~') + '/.config/bassa/'

# Utility function to read the README file.
def read(file_name):
    return open(os.path.join(os.path.dirname(__file__), file_name)).read()

base_dir = os.path.dirname(os.path.abspath(__file__))
requirements_path = os.path.join(base_dir, 'requirements.txt')

install_reqs = parse_requirements(requirements_path, session=False)

requirements = [str(ir.req) for ir in install_reqs]

### Set configs ###
if platform.system() == 'Linux':
    configdir = LINUX_CONFDIR
elif platform.system() == 'Windows':
    configdir = WIN_CONFDIR
elif platform.system() == 'Darwin':
    configdir = OSX_CONFDIR
if not os.path.exists(configdir):
    os.makedirs(configdir)

email_conf_location = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + "/" + EMAIL_CONF
dl_conf_location = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + "/" + DL_CONF
shutil.copyfile(email_conf_location, configdir + EMAIL_CONF)
shutil.copyfile(dl_conf_location, configdir + DL_CONF)

###/ Set configs ###

setup(
   ...
)

Please help me with this issue.


Solution

  • Update:

    * Please note that updating pip and pip-tools is not supported in my case. Then the workaround in my answer will help.

    * If updating pip and pip-tools to compatible version is supported then refer to Gnnr's answer or Heapify's answer

    I got the fix finally \o/

    install_reqs = parse_requirements(requirements_path, session=False)
    

    At first, I have inspected what install_reqs was on Travis by simply logging it and found that it was a list of ParsedRequirement objects. I also found that this class is defined in req_file.py. I have gone to check the source code for req_file.py here on GitHub. I found that there was no such attribute called req but instead it is requirement. So there were two versions of parse_requirements function so I handled this using a try and except block.

    # Generator must be converted to list, or we will only have one chance to read each element, meaning that the first requirement will be skipped.
    requirements = list(requirements) 
    try:
        requirements = [str(ir.req) for ir in install_reqs]
    except:
        requirements = [str(ir.requirement) for ir in install_reqs]
    

    Now it is compatible with both the versions \0/