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

Python metric collection #11013

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

sachin-sandhu
Copy link
Contributor

@sachin-sandhu sachin-sandhu commented Nov 25, 2024

What are you trying to accomplish?

Fix: Enables metric collection for pip-compile and pipenv
Enhancements to package manager detection:

Also enables local python setup for correct detection of pip-compile, poetry and pipenv based of python version

Anything you want to highlight for special attention from reviewers?

How will you know you've accomplished your goal?

Checklist

  • I have run the complete test suite to ensure all tests and linters pass.
  • I have thoroughly tested my code changes to ensure they work as expected, including adding additional tests for new functionality.
  • I have written clear and descriptive commit messages.
  • I have provided a detailed description of the changes in the pull request, including the problem it addresses, how it fixes the problem, and any relevant details about the implementation.
  • I have ensured that the code is well-documented and easy to understand.

@sachin-sandhu sachin-sandhu self-assigned this Nov 25, 2024
@sachin-sandhu sachin-sandhu marked this pull request as ready for review November 26, 2024 16:13
@sachin-sandhu sachin-sandhu requested a review from a team as a code owner November 26, 2024 16:13
@sachin-sandhu sachin-sandhu marked this pull request as draft November 26, 2024 16:18
@sachin-sandhu sachin-sandhu marked this pull request as ready for review November 26, 2024 16:29
nil
end

sig { returns(T.any(String, T.untyped)) }
Copy link
Member

Choose a reason for hiding this comment

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

why return untyped?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@abdulapopoola , This is for fail safe , As we are extracting package manager version from shell command, in case the output changes or there is any issue in version extraction (very low possibility) we can make sure metric collection exception does not cause update to fail. We are checking for untyped at "return PoetryPackageManager.new(detect_poetry_version) if poetry_files && detect_poetry_version"


return PipCompilePackageManager.new(detect_pipcompile_version) if pip_compile_files && detect_pipcompile_version

return PipenvPackageManager.new(detect_pipenv_version) if pipenv_files && detect_pipenv_version

PipPackageManager.new(detect_pip_version)
Copy link
Contributor

Choose a reason for hiding this comment

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

I am pretty sure there are python package managers we don't support yet, I think this should still follow the same logic and only return pip if pip is actually found. This method should return nil if no supported package managers are found.

Copy link
Contributor

@kbukum1 kbukum1 Nov 26, 2024

Choose a reason for hiding this comment

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

I am pretty sure there are python package managers we don't support yet, I think this should still follow the same logic and only return pip if pip is actually found. This method should return nil if no supported package managers are found.

@pavera
When you return ecosystem object in file_parser, currently package_manager is required field. If we are unable detect package_manager I believe we shouldn't be able to hit this method since this method is getting called before starting update operation after parsing dependency files. In this situation we should return error or something for not supported package manager.

@sachin-sandhu
Is there a way to be sure that we are returning the right package manager?

return PeotryPackageManager.new(detect_poetry_version) if poetry_lock && detect_poetry_version
setup_python_environment if Dependabot::Experiments.enabled?(:enable_file_parser_python_local)

return PoetryPackageManager.new(detect_poetry_version) if poetry_files && detect_poetry_version
Copy link
Contributor

Choose a reason for hiding this comment

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

the *_files checks are redundant here as the detect_*_version methods already check for the existence of the files.

end

def pipenv_files
requirement_files.any?("Pipfile")
Copy link
Member

Choose a reason for hiding this comment

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

should these be constants? Tagging @kbukum1 since he established a pattern in npm

Copy link
Contributor

@kbukum1 kbukum1 Nov 26, 2024

Choose a reason for hiding this comment

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

Hi @sachin-sandhu,

I recommend checking the implementation of the npm_and_yarn package manager. You'll notice that we've moved all string constants into their respective modules/classes, allowing them to be reused throughout the system. This approach ensures that the code is cleaner and easier to maintain.

For example, in the following snippet, constants are defined within their relevant ecosystem or package manager classes. This keeps the code modular and avoids hardcoding strings repeatedly across the system:

module Dependabot
  module NpmAndYarn
    ECOSYSTEM = "npm_and_yarn"
    MANIFEST_FILENAME = "package.json"
    ...
    MANIFEST_PACKAGE_MANAGER_KEY = "packageManager"
    MANIFEST_ENGINES_KEY = "engines"

    class NpmPackageManager < Ecosystem::VersionManager
      extend T::Sig
      NAME = "npm"
      RC_FILENAME = ".npmrc"
      LOCKFILE_NAME = "package-lock.json"
      SHRINKWRAP_LOCKFILE_NAME = "npm-shrinkwrap.json"
      ...
    end

    class YarnPackageManager < Ecosystem::VersionManager
      extend T::Sig
      NAME = "yarn"
      RC_FILENAME = ".yarnrc"
      RC_YML_FILENAME = ".yarnrc.yml"
      LOCKFILE_NAME = "yarn.lock"
      ...
    end

    class PNPMPackageManager < Ecosystem::VersionManager
      extend T::Sig
      NAME = "pnpm"
      LOCKFILE_NAME = "pnpm-lock.yaml"
      PNPM_WS_YML_FILENAME = "pnpm-workspace.yaml"
      ...
    end

    DEFAULT_PACKAGE_MANAGER = NpmPackageManager::NAME

    # Define a type alias for the expected class interface
    NpmAndYarnPackageManagerClassType = T.type_alias do
      T.any(
        T.class_of(Dependabot::NpmAndYarn::NpmPackageManager),
        T.class_of(Dependabot::NpmAndYarn::YarnPackageManager),
        T.class_of(Dependabot::NpmAndYarn::PNPMPackageManager)
      )
    end
  end
end

By organizing constants this way, you can easily reference them across the system while maintaining a clean and structured design. Let me know if you have any questions!

Reference: https://github.com/dependabot/dependabot-core/blob/main/npm_and_yarn/lib/dependabot/npm_and_yarn/package_manager.rb#L11

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants