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
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions etc/scripts/deps_info.py
Original file line number Diff line number Diff line change
@@ -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.

Consider instead search-package.py

#
# Copyright nexB Inc. and others. All rights reserved.
# http://nexb.com and https://github.com/nexB/scancode-toolkit/
# The ScanCode software is licensed under the Apache License version 2.0.
# Data generated with ScanCode require an acknowledgment.
# ScanCode is a trademark of nexB Inc.
#
# You may not use this software except in compliance with the License.
# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# When you publish or redistribute any data created with ScanCode or any ScanCode
# derivative work, you must accompany this data with the following acknowledgment:
#
# Generated with ScanCode and provided on an "AS IS" BASIS, WITHOUT WARRANTIES
# OR CONDITIONS OF ANY KIND, either express or implied. No content created from
# ScanCode should be considered or used as legal advice. Consult an Attorney
# for any legal advice.
# ScanCode is a free software code scanning tool from nexB Inc. and others.
# Visit https://github.com/nexB/scancode-toolkit/ for support and download.

from __future__ import absolute_import
from __future__ import print_function

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):
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

"""
Search specific package in given directory with all corresponding files.
Abhishek-Dev09-zz marked this conversation as resolved.
Show resolved Hide resolved
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.

"""

if version:
package_name = "*{}-{}*".format(package_name, version)
else:
package_name = "*{}*".format(package_name)
thirdparty = resource_iter(target, with_dirs=False)
dependency = [
files for files in thirdparty if fnmatch.fnmatchcase(files, package_name)
]
if dependency:
whl = [
files for files in dependency if files.endswith(".whl")
] ## There are multiple version of same package So list of wheel will be considered.
sdist = [files for files in dependency if files.endswith(".tar.gz")]
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")
print("\n")
if sdist:
print(*sdist, sep="\n")
else:
print("Corresponding sdist does not exits in target\n")
print("\n")
if about:
print(*about, sep="\n")
else:
print("Corresponding .ABOUT does not exits in target\n")
print("\n")
if license:
print(*licence, sep="\n")
else:
print("Corresponding .LICENSE does not exits in target\n")
print("\n")
if notice:
print(*notice, sep="\n")
else:
print("Corresponding .NOTICE does not exits in target\n")
print("\n")
else:
print("Specified package does not exist\n")


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.

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

--fetch PACKAGE_NAME \\
--target TARGET_DIR \\
--version VERSION_OF_PACKAGE \\
""",
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 as this is not helping with readability of the help and is redundant with argparse's printouts

formatter_class=argparse.RawDescriptionHelpFormatter,
)

parser.add_argument(
"--fetch",
help="Required: Specific Dependencies to be fetched.",
type=str,
required=True,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider this instead:


    parser.add_argument(
        "--name",
        help="Name of the package to search in the TARGET_DIR directory",
        type=str,
        required=True,
    )


parser.add_argument(
"--target",
Copy link
Contributor

Choose a reason for hiding this comment

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

Use directory instead

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]"

type=str,
default="thirdparty",
)

parser.add_argument(
"--version",
help="Specify version of dependencies to 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 "Optional package version to search."

type=str,
default=None,
)

args = parser.parse_args()

package_name = args.fetch
Copy link
Contributor

Choose a reason for hiding this comment

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

rename fetch everywhere to package_name

target = args.target
version = args.version
search_package(package_name, target, version)


def main() -> None:
main_with_args(sys.argv[1:])


if __name__ == "__main__":
main()
134 changes: 134 additions & 0 deletions etc/scripts/freeze_and_update_reqs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# -*- coding: utf-8 -*-
#
# Copyright nexB Inc. and others. All rights reserved.
# http://nexb.com and https://github.com/nexB/scancode-toolkit/
# The ScanCode software is licensed under the Apache License version 2.0.
# Data generated with ScanCode require an acknowledgment.
# ScanCode is a trademark of nexB Inc.
#
# You may not use this software except in compliance with the License.
# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# When you publish or redistribute any data created with ScanCode or any ScanCode
# derivative work, you must accompany this data with the following acknowledgment:
#
# Generated with ScanCode and provided on an "AS IS" BASIS, WITHOUT WARRANTIES
# OR CONDITIONS OF ANY KIND, either express or implied. No content created from
# ScanCode should be considered or used as legal advice. Consult an Attorney
# for any legal advice.
# ScanCode is a free software code scanning tool from nexB Inc. and others.
# Visit https://github.com/nexB/scancode-toolkit/ for support and download.

from __future__ import absolute_import
from __future__ import print_function

import argparse
import fnmatch
from commoncode.fileutils import resource_iter
import os
import subprocess
import shutil
import sys

python_version = str(sys.version_info[0]) + str(sys.version_info[1])
py_abi = "{0}cp{1}{0}".format("*", python_version)


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 a `package_name` is provided it will be updated to its latest version.
"""
thirdparty = resource_iter(input_dir, with_dirs=False)
# FIXME this code is for py 3.6 and later we will update for all version
dependencies = [
files
for files in thirdparty
if fnmatch.fnmatchcase(files, "*py3*")
or fnmatch.fnmatchcase(files, py_abi)
or (
fnmatch.fnmatchcase(files, "*tar.gz*")
and not fnmatch.fnmatchcase(files, "*py2-ipaddress-3.4.1.tar.gz*")
)
]
Abhishek-Dev09-zz marked this conversation as resolved.
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

for deps in dependencies:
shutil.copy(deps, "required_deps")
subprocess.run(
[
"pip-compile",
"--generate-hashes",
"--find-links",
"required_deps",
"--upgrade",
"--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

"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.

"--pip-args",
"--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



def main_with_args(args: str) -> None:
parser = argparse.ArgumentParser(
description="""Creates a archive for specific OS and specific python.
EXAMPLE:
freeze_and_update_reqs.py \\
--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.

formatter_class=argparse.RawDescriptionHelpFormatter,
)

parser.add_argument(
"--deps_directory",
help="Required: Thirdparty Dependencies directory to be archived.",
type=str,
required=True,
)

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.

type=str,
pombredanne marked this conversation as resolved.
Show resolved Hide resolved
default="requirements.txt",
)

parser.add_argument(
"--upgrade",
help="Upgrade all dependencies to new version.",
action="store_true",
)

parser.add_argument(
"--upgrade_package",
help="Specify particular packages to upgrade.",
type=str,
default=None,
)

args = parser.parse_args()

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.

generate_req_text(tpdir, output_file, 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.

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`


def main() -> None:
main_with_args(sys.argv[1:])


if __name__ == "__main__":
main()