Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automate the creation of requirement.txt and fetch deps #2118

Merged

Conversation

Abhishek-Dev09-zz
Copy link

@Abhishek-Dev09-zz Abhishek-Dev09-zz commented Jul 20, 2020

By running this script, we will able to create and update the requirements.txt by providing the input directory like thirdparty .

This script will fetch dependencies with all corresponding files(.whl,.tar.gz,.LICENCE,.NOTICE,.ABOUT) from the given target . This feature will ask ScanCode's developer to provide dependencies as package_name,version and target directory where the built wheels and tarballs would be fetched.
Signed-off-by: Abhishek Kumar abhishek.kasyap09@gmail.com

Fixes aboutcode-org/skeleton#50 #2087

By running this script, we will able to create and update the requirements.txt by providing the input directory like thirdparty . Also adding deps setuptools which is deps of zc.lockfile-2.0

Signed-off-by: Abhishek Kumar <abhishek.kasyap09@gmail.com>
Identify Python version to extract abi of wheels which helps us in filtering the required deps.

Signed-off-by: Abhishek Kumar <abhishek.kasyap09@gmail.com>
This script will fetch dependencies with all corresponding files(.whl,.tar.gz,.LICENCE,.NOTICE,.ABOUT) from the given target . This feature will ask  ScanCode's developer to provide dependencies as package_name,version and target directory where the built wheels and tarballs would be fetched.

Signed-off-by: Abhishek Kumar <abhishek.kasyap09@gmail.com>
@Abhishek-Dev09-zz Abhishek-Dev09-zz changed the title Automate the creation of requirement.txt Automate the creation of requirement.txt and fetch deps Jul 21, 2020
Copy link
Contributor

@pombredanne pombredanne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks... see my comments!

etc/scripts/freeze_and_update_reqs.py Show resolved Hide resolved
)
]
if not (os.path.isdir("required_deps")):
os.mkdir("required_deps")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you create that directory?

Copy link
Author

@Abhishek-Dev09-zz Abhishek-Dev09-zz Jul 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To filter the dependencies of specific python version we want because we have both py2 and py3 wheels in thirdparty. Don't worry it will deleted later by scripts itself.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so please add a comment explaining that... and also this may not be created where you want. Make sure you use a well known location or better use some temp directory instead since this is temporary

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean to say bin

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use a temp dir instead

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -0,0 +1,128 @@
# -*- coding: utf-8 -*-
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not name this scancode ;) that's confusing!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest the better name.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deps_info.py

about = [files for files in dependency if files.endswith(".ABOUT")]
notice = [files for files in dependency if files.endswith(".NOTICE")]
license = [files for files in dependency if files.endswith(".LICENSE")]
print(*whl, sep="\n")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you print all that ?

Copy link
Author

@Abhishek-Dev09-zz Abhishek-Dev09-zz Jul 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because scancode's developer want to fetch the deps in target directory. Script will know developer by printing the info of deps.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's too verbose by default. Hide the prints behind an option

Copy link
Author

@Abhishek-Dev09-zz Abhishek-Dev09-zz Jul 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it shows ONLY .ABOUT/.NOTICE/.LICENCE/.whl/.tar.gz . Not too verbose i have seen

@@ -0,0 +1,15 @@
about_resource: setuptools-41.2.0-py2.py3-none-any.whl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should already have setuptools..... good catch. But please do not commit it there, craft another PR instead just for that.

Copy link
Author

@Abhishek-Dev09-zz Abhishek-Dev09-zz Jul 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't worry, i already mentioned in PR description. FYI there is no deps named setuptools . That's why i added wheels setuptools also here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get that, but please provide a different PR for adding that back (and FYI zopefoundation/zc.lockfile#22 )

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed and PR #2123

Copy link
Contributor

@pombredanne pombredanne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks... please see my comments inline

"--output-file",
output_file,
"--verbose",
"--upgrade-package",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you do this? we never want to do some unwanted upgrade-package IMHO and what is "package_name", below about?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this feature alow you to upgrade specfic package. See help by running this arg.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I known that but what is "package_name" I know of no package whose name is "package_name"
If this is some kind of magic, explain it in comments, do not make me chase down the help of that command

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any case why would I want to upgrade a package there? Please elaborate and explain in comments

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Package name may be intbitset, banal, urlpy . Anything deps that exist in thirdparty


parser.add_argument(
"--upgrade",
help="Upgrade all dependencies to new version. ",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you have trailing whitespaces here and elsewhere

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

"""
Generate a requirement.txt file of all dependencies present in thirdparty.
"""
thirdparty = list(resource_iter(input_dir, with_dirs=False))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list() wrapper is not needed here

Copy link
Author

@Abhishek-Dev09-zz Abhishek-Dev09-zz Jul 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


def generate_req_text(input_dir, output_file=False, package_name=False):
"""
Generate a requirement.txt file of all dependencies present in thirdparty.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and what are the args?
this should IMHO be something such as:

Generate a requirement file at `output_file` of all dependencies wheels and sdists present in the `input_dir` directory.
If a `package_name` is provided it will be updated to its latest version.

though I am not sure this is true for package_name
Also output_file=False, package_name=False is weird. These should be None... and output_file should always be required

Also what is the base used there? the setup.py or some requirements.in file? I think we will need a requirements.in file

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Base used here : setup.py and no need of requirements.in file. And by default output_file is requirements.txt and it is default arguments.

about = [files for files in dependency if files.endswith(".ABOUT")]
notice = [files for files in dependency if files.endswith(".NOTICE")]
license = [files for files in dependency if files.endswith(".LICENSE")]
print(*whl, sep="\n")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's too verbose by default. Hide the prints behind an option

@@ -0,0 +1,15 @@
about_resource: setuptools-41.2.0-py2.py3-none-any.whl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get that, but please provide a different PR for adding that back (and FYI zopefoundation/zc.lockfile#22 )

Signed-off-by: Abhishek Kumar <abhishek.kasyap09@gmail.com>
Signed-off-by: Abhishek Kumar <abhishek.kasyap09@gmail.com>
Copy link
Contributor

@pombredanne pombredanne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks... see my comments

etc/scripts/deps_info.py Outdated Show resolved Hide resolved

def main_with_args(args: str) -> None:
parser = argparse.ArgumentParser(
description="""Fetch a specific package with version in given target like thirdparty by default.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thsi command seems if fetches nothing at all, does it?

Copy link
Author

@Abhishek-Dev09-zz Abhishek-Dev09-zz Jul 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it description of function , you know this thing by running --help option. I help developer when he put --help option

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be instead:

Search PACKAGE_NAME (with an optional VERSION_OF_PACKAGE) in the TARGET_DIR directory. 
Print results on screen.


def generate_req_text(input_dir, output_file=None, package_name=None):
"""
Generate a requirement file at `output_file`(by default requirements.txt) of all dependencies wheels and sdists present in the `input_dir` directory.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please wrap this. Also if you want a default of (by default requirements.txt)... then set the default in the function definition and not as None

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already set as defaults.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By wrap, wrap the very long lines.

)
]
if not (os.path.isdir("required_deps")):
os.mkdir("required_deps")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use a temp dir instead

"--output-file",
output_file,
"--verbose",
"--upgrade-package",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I known that but what is "package_name" I know of no package whose name is "package_name"
If this is some kind of magic, explain it in comments, do not make me chase down the help of that command

"--output-file",
output_file,
"--verbose",
"--upgrade-package",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any case why would I want to upgrade a package there? Please elaborate and explain in comments

"--no-index",
]
)
shutil.rmtree("required_deps")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather you use a temp file with a "with" context manager to avoid leaving dangling files

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not a file , it is directory


parser.add_argument(
"--output",
help="Output file name. Required if more than one input file is given. Will be derived from input file otherwise.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid implicit, derived name. It is preferred to always require an input instead

Copy link
Author

@Abhishek-Dev09-zz Abhishek-Dev09-zz Jul 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pip-tools uses --output-file for same purpose why should i not not use --output?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point is to avoid using a derived name. Instead make this always required and not implied.

Copy link
Author

@Abhishek-Dev09-zz Abhishek-Dev09-zz Jul 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok , your call i changing the option --output to --requirement . And making always required.

tpdir = args.deps_directory
output_file = args.output
package_name = args.upgrade_package
upgrade = args.upgrade or None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This argument is not used at all... so what is it for?

Copy link
Author

@Abhishek-Dev09-zz Abhishek-Dev09-zz Jul 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upgrade is for upgrading all packages whereas upgrade_package is for specific package,

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But where do you use it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is not an argument used anywhere in your functions

Copy link
Author

@Abhishek-Dev09-zz Abhishek-Dev09-zz Jul 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch .It is an option just like ON/OFF . It is used here only , i have tested it it works, if you want i will paste output here.

Signed-off-by: Abhishek Kumar <abhishek.kasyap09@gmail.com>
Signed-off-by: Abhishek Kumar <abhishek.kasyap09@gmail.com>
"--verbose",
"--allow-unsafe",
"--upgrade-package",
"package_name",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"package_name" is NOT a variable here ... but a plain string. This cannot work as it is... you need to test your code.

Copy link
Author

@Abhishek-Dev09-zz Abhishek-Dev09-zz Jul 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yeah it is function parameter through arguments . I have treated like temp dir as That is also also in double quotes but it is dir not variable . Same issue with upgrade parameter also.

Signed-off-by: Abhishek Kumar <abhishek.kasyap09@gmail.com>
Signed-off-by: Abhishek Kumar <abhishek.kasyap09@gmail.com>
parser = argparse.ArgumentParser(
description="""Fetch a specific package with version in given target like thirdparty by default.
EXAMPLE:
scancode.py \\
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deps

Copy link
Contributor

@pombredanne pombredanne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my nitpickings inline for your review!
Thanks you for your persistence!

import sys


def search_package(package_name, target, version):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please version=None since this is optional

import argparse
import fnmatch
from commoncode.fileutils import resource_iter
import sys
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sort imports


def search_package(package_name, target, version):
"""
Search specific package in given directory with all corresponding files.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use instead:

Search `package_name` (with an optional `version`) in the `target` directory. Print results on screen.


def main_with_args(args: str) -> None:
parser = argparse.ArgumentParser(
description="""Fetch a specific package with version in given target like thirdparty by default.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be instead:

Search PACKAGE_NAME (with an optional VERSION_OF_PACKAGE) in the TARGET_DIR directory. 
Print results on screen.


parser.add_argument(
"--target",
help=" a target directory where the built wheels and tarballs would be fetched.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use this:
"Directory to search for package wheels and tarballs. [default: thirdparty]"


def generate_req_text(input_dir, output_file=None, package_name=None):
"""
Generate a requirement file at `output_file`(by default requirements.txt) of all dependencies wheels and sdists present in the `input_dir` directory.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By wrap, wrap the very long lines.

'pip-compile',
'--generate-hashes',
'--find-links',
'temp dir',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if package_name:
pip_args.extend(['--upgrade-package', package_name])
run(pip_args)
rmtree('temp dir')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With a proper https://docs.python.org/3/library/tempfile.html?highlight=tempfile#module-tempfile you will NOT need to rm anything, with will be handled for you if you use a context manager.
such as this example from the Python doc:

# create a temporary directory using the context manager
>>> with tempfile.TemporaryDirectory() as tmpdirname:
...     print('created temporary directory', tmpdirname)
 .... do your things here

--deps_directory DEPS_DIRECTORY \\
--output OUTPUT \\
--upgrade_package PACKAGE_NAME \\
""",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this example.

package_name = args.upgrade_package
upgrade = args.upgrade or False
generate_req_text(tpdir, requirement_file, package_name, upgrade)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer named keyword arguments for example:
(tpdir=tpdir, requirement_file=requirement_file, package_name=package_name, upgrade=upgrade) But do not use these names... use the same variable names on both sides: e.g. if you use args.deps_directory then usedeps_directoryand nottpdir`

from fnmatch import fnmatchcase
import os
from subprocess import run
from shutil import copy, rmtree
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpicking: your imports are not sorted. Try to use https://pypi.org/project/isort/

Copy link
Author

@Abhishek-Dev09-zz Abhishek-Dev09-zz Jul 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh , i missed that .Also i have to remove unused import

def generate_req_text(find_links, req_file, package_name=None, upgrade=False):
"""
Generate a requirement file at `req_file` of all dependencies wheels and
sdists present in the `input_dir` directory.If a `package_name` is provided
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no input_dir?
...sdists present in the input_dir directory...
Do you mind doing an extra review pass on this PR and #2117 to make sure the docstrings are well synced with the actual function arguments and what the function does>

Copy link
Author

@Abhishek-Dev09-zz Abhishek-Dev09-zz Jul 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, this PR has reviewed many times. I would like work on diff PR

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Signed-off-by: Abhishek Kumar <abhishek.kasyap09@gmail.com>
Signed-off-by: Philippe Ombredanne <pombredanne@nexb.com>
Copy link
Contributor

@pombredanne pombredanne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking fine. Thank you! merging

@pombredanne pombredanne merged commit 7036274 into aboutcode-org:develop Jul 27, 2020
@Abhishek-Dev09-zz Abhishek-Dev09-zz deleted the #295-requirements.txt branch July 27, 2020 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Dev utility: Create script to generate requirements with hashes
3 participants