Skip to content

Commit

Permalink
importlib.metadata version and entry_points logic (#898)
Browse files Browse the repository at this point in the history
* Replaced pkg_resources with importlib.metadata

* Fix entry_points logic for Py312

* Fix logic for entry_points

* Check to see if list or string
  • Loading branch information
lrafeei committed Aug 16, 2023
1 parent f1a673e commit d44d294
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 115 deletions.
70 changes: 39 additions & 31 deletions newrelic/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,26 @@

from __future__ import print_function

import sys
import logging
import sys

_builtin_plugins = [
'debug_console',
'generate_config',
'license_key',
'local_config',
'network_config',
'record_deploy',
'run_program',
'run_python',
'server_config',
'validate_config'
"debug_console",
"generate_config",
"license_key",
"local_config",
"network_config",
"record_deploy",
"run_program",
"run_python",
"server_config",
"validate_config",
]

_commands = {}


def command(name, options='', description='', hidden=False,
log_intercept=True, deprecated=False):
def command(name, options="", description="", hidden=False, log_intercept=True, deprecated=False):
def wrapper(callback):
callback.name = name
callback.options = options
Expand All @@ -44,22 +43,23 @@ def wrapper(callback):
callback.deprecated = deprecated
_commands[name] = callback
return callback

return wrapper


def usage(name):
details = _commands[name]
if details.deprecated:
print("[WARNING] This command is deprecated and will be removed")
print('Usage: newrelic-admin %s %s' % (name, details.options))
print("Usage: newrelic-admin %s %s" % (name, details.options))


@command('help', '[command]', hidden=True)
@command("help", "[command]", hidden=True)
def help(args):
if not args:
print('Usage: newrelic-admin command [options]')
print("Usage: newrelic-admin command [options]")
print()
print("Type 'newrelic-admin help <command>'", end='')
print("Type 'newrelic-admin help <command>'", end="")
print("for help on a specific command.")
print()
print("Available commands are:")
Expand All @@ -68,24 +68,24 @@ def help(args):
for name in commands:
details = _commands[name]
if not details.hidden:
print(' ', name)
print(" ", name)

else:
name = args[0]

if name not in _commands:
print("Unknown command '%s'." % name, end=' ')
print("Unknown command '%s'." % name, end=" ")
print("Type 'newrelic-admin help' for usage.")

else:
details = _commands[name]

print('Usage: newrelic-admin %s %s' % (name, details.options))
print("Usage: newrelic-admin %s %s" % (name, details.options))
if details.description:
print()
description = details.description
if details.deprecated:
description = '[DEPRECATED] ' + description
description = "[DEPRECATED] " + description
print(description)


Expand All @@ -99,37 +99,45 @@ def emit(self, record):
if len(logging.root.handlers) != 0:
return

if record.name.startswith('newrelic.packages'):
if record.name.startswith("newrelic.packages"):
return

if record.levelno < logging.WARNING:
return

return logging.StreamHandler.emit(self, record)

_stdout_logger = logging.getLogger('newrelic')
_stdout_logger = logging.getLogger("newrelic")
_stdout_handler = FilteredStreamHandler(sys.stdout)
_stdout_format = '%(levelname)s - %(message)s\n'
_stdout_format = "%(levelname)s - %(message)s\n"
_stdout_formatter = logging.Formatter(_stdout_format)
_stdout_handler.setFormatter(_stdout_formatter)
_stdout_logger.addHandler(_stdout_handler)


def load_internal_plugins():
for name in _builtin_plugins:
module_name = '%s.%s' % (__name__, name)
module_name = "%s.%s" % (__name__, name)
__import__(module_name)


def load_external_plugins():
try:
import pkg_resources
# Preferred after Python 3.10
if sys.version_info >= (3, 10):
from importlib.metadata import entry_points
# Introduced in Python 3.8
elif sys.version_info >= (3, 8) and sys.version_info <= (3, 9):
from importlib_metadata import entry_points
# Removed in Python 3.12
else:
from pkg_resources import iter_entry_points as entry_points
except ImportError:
return

group = 'newrelic.admin'
group = "newrelic.admin"

for entrypoint in pkg_resources.iter_entry_points(group=group):
for entrypoint in entry_points(group=group):
__import__(entrypoint.module_name)


Expand All @@ -138,12 +146,12 @@ def main():
if len(sys.argv) > 1:
command = sys.argv[1]
else:
command = 'help'
command = "help"

callback = _commands[command]

except Exception:
print("Unknown command '%s'." % command, end='')
print("Unknown command '%s'." % command, end="")
print("Type 'newrelic-admin help' for usage.")
sys.exit(1)

Expand All @@ -156,5 +164,5 @@ def main():
load_internal_plugins()
load_external_plugins()

if __name__ == '__main__':
if __name__ == "__main__":
main()
5 changes: 4 additions & 1 deletion newrelic/common/package_version_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ def _get_package_version(name):
if "importlib" in sys.modules and hasattr(sys.modules["importlib"], "metadata"):
try:
# In Python3.10+ packages_distribution can be checked for as well
if hasattr(sys.modules["importlib"].metadata, "packages_distributions"): # pylint: disable=E1101
if hasattr(sys.modules["importlib"].metadata, "packages_distributions"): # pylint: disable=E1101
distributions = sys.modules["importlib"].metadata.packages_distributions() # pylint: disable=E1101
distribution_name = distributions.get(name, name)
distribution_name = distribution_name[0] if isinstance(distribution_name, list) else distribution_name
else:
distribution_name = name

Expand All @@ -87,6 +88,7 @@ def _get_package_version(name):
except Exception:
pass

# __version__ has been deprecated in Python 3.12 and will be removed in future versions
for attr in VERSION_ATTRS:
try:
version = getattr(module, attr, None)
Expand All @@ -101,6 +103,7 @@ def _get_package_version(name):
except Exception:
pass

# pkg_resources has been removed in Python 3.12
if "pkg_resources" in sys.modules:
try:
version = sys.modules["pkg_resources"].get_distribution(name).version
Expand Down
24 changes: 20 additions & 4 deletions newrelic/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3180,13 +3180,21 @@ def _process_module_builtin_defaults():

def _process_module_entry_points():
try:
import pkg_resources
# Preferred after Python 3.10
if sys.version_info >= (3, 10):
from importlib.metadata import entry_points
# Introduced in Python 3.8
elif sys.version_info >= (3, 8) and sys.version_info <= (3, 9):
from importlib_metadata import entry_points
# Removed in Python 3.12
else:
from pkg_resources import iter_entry_points as entry_points
except ImportError:
return

group = "newrelic.hooks"

for entrypoint in pkg_resources.iter_entry_points(group=group):
for entrypoint in entry_points(group=group):
target = entrypoint.name

if target in _module_import_hook_registry:
Expand Down Expand Up @@ -3244,13 +3252,21 @@ def _setup_instrumentation():

def _setup_extensions():
try:
import pkg_resources
# Preferred after Python 3.10
if sys.version_info >= (3, 10):
from importlib.metadata import entry_points
# Introduced in Python 3.8
elif sys.version_info >= (3, 8) and sys.version_info <= (3, 9):
from importlib_metadata import entry_points
# Removed in Python 3.12
else:
from pkg_resources import iter_entry_points as entry_points
except ImportError:
return

group = "newrelic.extension"

for entrypoint in pkg_resources.iter_entry_points(group=group):
for entrypoint in entry_points(group=group):
__import__(entrypoint.module_name)
module = sys.modules[entrypoint.module_name]
module.initialize()
Expand Down
54 changes: 25 additions & 29 deletions newrelic/hooks/framework_pyramid.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,23 @@
from newrelic.api.transaction import current_transaction
from newrelic.api.wsgi_application import wrap_wsgi_application
from newrelic.common.object_names import callable_name
from newrelic.common.object_wrapper import (FunctionWrapper, wrap_out_function,
wrap_function_wrapper)
from newrelic.common.object_wrapper import (
FunctionWrapper,
wrap_function_wrapper,
wrap_out_function,
)
from newrelic.common.package_version_utils import get_package_version


def instrument_pyramid_router(module):
pyramid_version = None
pyramid_version = get_package_version("pyramid")

try:
import pkg_resources
pyramid_version = pkg_resources.get_distribution('pyramid').version
except Exception:
pass

wrap_wsgi_application(
module, 'Router.__call__', framework=('Pyramid', pyramid_version))
wrap_wsgi_application(module, "Router.__call__", framework=("Pyramid", pyramid_version))


def status_code(exc, value, tb):
from pyramid.httpexceptions import HTTPException

# Ignore certain exceptions based on HTTP status codes.

if isinstance(value, HTTPException):
Expand All @@ -75,6 +73,7 @@ def status_code(exc, value, tb):

def should_ignore(exc, value, tb):
from pyramid.exceptions import PredicateMismatch

# Always ignore PredicateMismatch as it is raised by views to force
# subsequent views to be consulted when multi views are being used.
# It isn't therefore strictly an error as such as a subsequent view
Expand All @@ -100,9 +99,7 @@ def view_handler_wrapper(wrapped, instance, args, kwargs):

# set exception views to priority=1 so they won't take precedence over
# the original view callable
transaction.set_transaction_name(
name,
priority=1 if args and isinstance(args[0], Exception) else 2)
transaction.set_transaction_name(name, priority=1 if args and isinstance(args[0], Exception) else 2)

with FunctionTrace(name, source=view_callable) as trace:
try:
Expand All @@ -114,7 +111,7 @@ def view_handler_wrapper(wrapped, instance, args, kwargs):


def wrap_view_handler(mapped_view):
if hasattr(mapped_view, '_nr_wrapped'):
if hasattr(mapped_view, "_nr_wrapped"):
return mapped_view
else:
wrapped = FunctionWrapper(mapped_view, view_handler_wrapper)
Expand Down Expand Up @@ -157,7 +154,7 @@ def _wrapper(context, request):
return wrapper(context, request)
finally:
attr = instance.attr
inst = getattr(request, '__view__', None)
inst = getattr(request, "__view__", None)
if inst is not None:
if attr:
handler = getattr(inst, attr)
Expand All @@ -166,7 +163,7 @@ def _wrapper(context, request):
tracer.name = name
tracer.add_code_level_metrics(handler)
else:
method = getattr(inst, '__call__')
method = getattr(inst, "__call__")
if method:
name = callable_name(method)
transaction.set_transaction_name(name, priority=2)
Expand All @@ -180,22 +177,21 @@ def instrument_pyramid_config_views(module):
# Location of the ViewDeriver class changed from pyramid.config to
# pyramid.config.views so check if present before trying to update.

if hasattr(module, 'ViewDeriver'):
wrap_out_function(module, 'ViewDeriver.__call__', wrap_view_handler)
elif hasattr(module, 'Configurator'):
wrap_out_function(module, 'Configurator._derive_view',
wrap_view_handler)
if hasattr(module, "ViewDeriver"):
wrap_out_function(module, "ViewDeriver.__call__", wrap_view_handler)
elif hasattr(module, "Configurator"):
wrap_out_function(module, "Configurator._derive_view", wrap_view_handler)

if hasattr(module, 'DefaultViewMapper'):
if hasattr(module, "DefaultViewMapper"):
module.DefaultViewMapper.map_class_requestonly = FunctionWrapper(
module.DefaultViewMapper.map_class_requestonly,
default_view_mapper_wrapper)
module.DefaultViewMapper.map_class_requestonly, default_view_mapper_wrapper
)
module.DefaultViewMapper.map_class_native = FunctionWrapper(
module.DefaultViewMapper.map_class_native,
default_view_mapper_wrapper)
module.DefaultViewMapper.map_class_native, default_view_mapper_wrapper
)


def instrument_pyramid_config_tweens(module):
wrap_function_wrapper(module, 'Tweens.add_explicit', wrap_add_tween)
wrap_function_wrapper(module, "Tweens.add_explicit", wrap_add_tween)

wrap_function_wrapper(module, 'Tweens.add_implicit', wrap_add_tween)
wrap_function_wrapper(module, "Tweens.add_implicit", wrap_add_tween)
Loading

0 comments on commit d44d294

Please sign in to comment.