From 8cfeb156df48ff8972f954f0dc357ad0eec4eee3 Mon Sep 17 00:00:00 2001 From: David Arnold Date: Tue, 8 Oct 2024 18:56:10 +0200 Subject: [PATCH] devx: add deprecation dumpster (#27887) * feat: Add deprecation_dumpster.py file * docs: add jovial and jocose docstring for frappe/deprecation_dumpster.py * refactor: fill the dumpster with its own kind * refactor: move to the deprecation dumpster * chore: color coding class * fix: only check import error when import errors --- frappe/__init__.py | 18 +- frappe/app.py | 7 +- frappe/auth.py | 5 +- frappe/client.py | 24 +- frappe/core/doctype/user/user.py | 18 +- frappe/database/database.py | 14 +- frappe/deprecation_dumpster.py | 290 ++++++++++++++++++ .../desk/doctype/bulk_update/bulk_update.py | 6 +- .../email/doctype/email_queue/email_queue.py | 11 +- frappe/geo/country_info.py | 5 +- frappe/handler.py | 8 +- frappe/utils/__init__.py | 39 +-- frappe/utils/background_jobs.py | 13 +- frappe/utils/deprecations.py | 29 +- frappe/utils/print_format.py | 7 +- 15 files changed, 361 insertions(+), 133 deletions(-) create mode 100644 frappe/deprecation_dumpster.py diff --git a/frappe/__init__.py b/frappe/__init__.py index 4e32a24994..9872103e8b 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -326,19 +326,23 @@ def connect(site: str | None = None, db_name: str | None = None, set_admin_as_us from frappe.database import get_db if site: - from frappe.utils.deprecations import deprecation_warning + from frappe.deprecation_dumpster import deprecation_warning deprecation_warning( + "unknown", + "v17", "Calling frappe.connect with the site argument is deprecated and will be removed in next major version. " - "Instead, explicitly invoke frappe.init(site) prior to calling frappe.connect(), if initializing the site is necessary." + "Instead, explicitly invoke frappe.init(site) prior to calling frappe.connect(), if initializing the site is necessary.", ) init(site) if db_name: - from frappe.utils.deprecations import deprecation_warning + from frappe.deprecation_dumpster import deprecation_warning deprecation_warning( + "unknown", + "v17", "Calling frappe.connect with the db_name argument is deprecated and will be removed in next major version. " - "Instead, explicitly invoke frappe.init(site) with the right config prior to calling frappe.connect(), if necessary." + "Instead, explicitly invoke frappe.init(site) with the right config prior to calling frappe.connect(), if necessary.", ) assert db_name or local.conf.db_user, "site must be fully initialized, db_user missing" @@ -1210,9 +1214,11 @@ def generate_hash(txt: str | None = None, length: int = 56) -> str: import secrets if txt: - from frappe.utils.deprecations import deprecation_warning + from frappe.deprecation_dumpster import deprecation_warning - deprecation_warning("The `txt` parameter is deprecated and will be removed in a future release.") + deprecation_warning( + "unknown", "v17", "The `txt` parameter is deprecated and will be removed in a future release." + ) return secrets.token_hex(math.ceil(length / 2))[:length] diff --git a/frappe/app.py b/frappe/app.py index 1abc74fb7e..e1693c0ec2 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -27,7 +27,6 @@ from frappe.auth import SAFE_HTTP_METHODS, UNSAFE_HTTP_METHODS, HTTPRequest, val from frappe.middlewares import StaticDataMiddleware from frappe.utils import CallbackManager, cint, get_site_name from frappe.utils.data import escape_html -from frappe.utils.deprecations import deprecation_warning from frappe.utils.error import log_error_snapshot from frappe.website.serve import get_response @@ -105,8 +104,12 @@ def application(request: Request): response = Response() elif frappe.form_dict.cmd: + from frappe.deprecation_dumpster import deprecation_warning + deprecation_warning( - f"{frappe.form_dict.cmd}: Sending `cmd` for RPC calls is deprecated, call REST API instead `/api/method/cmd`" + "unknown", + "v17", + f"{frappe.form_dict.cmd}: Sending `cmd` for RPC calls is deprecated, call REST API instead `/api/method/cmd`", ) frappe.handler.handle() response = frappe.utils.response.build_response("json") diff --git a/frappe/auth.py b/frappe/auth.py index 8652a7ba77..598e98e3e0 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -22,7 +22,6 @@ from frappe.twofactor import ( should_run_2fa, ) from frappe.utils import cint, date_diff, datetime, get_datetime, today -from frappe.utils.deprecations import deprecation_warning from frappe.utils.password import check_password, get_decrypted_password from frappe.website.utils import get_home_page @@ -518,7 +517,9 @@ class LoginAttemptTracker: :param lock_interval: Locking interval incase of maximum failed attempts """ if user_name: - deprecation_warning("`username` parameter is deprecated, use `key` instead.") + from frappe.deprecation_dumpster import deprecation_warning + + deprecation_warning("unknown", "v17", "`username` parameter is deprecated, use `key` instead.") self.key = key or user_name self.lock_interval = datetime.timedelta(seconds=lock_interval) self.max_failed_logins = max_consecutive_login_attempts diff --git a/frappe/client.py b/frappe/client.py index 028df862c4..f3d56067f8 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -12,7 +12,6 @@ from frappe.desk.reportview import validate_args from frappe.model.db_query import check_parent_permission from frappe.model.utils import is_virtual_doctype from frappe.utils import get_safe_filters -from frappe.utils.deprecations import deprecated if TYPE_CHECKING: from frappe.model.document import Document @@ -328,28 +327,9 @@ def get_password(doctype, name, fieldname): return frappe.get_doc(doctype, name).get_password(fieldname) -@frappe.whitelist() -@deprecated -def get_js(items): - """Load JS code files. Will also append translations - and extend `frappe._messages` +from frappe.deprecation_dumpster import get_js as _get_js - :param items: JSON list of paths of the js files to be loaded.""" - items = json.loads(items) - out = [] - for src in items: - src = src.strip("/").split("/") - - if ".." in src or src[0] != "assets": - frappe.throw(_("Invalid file path: {0}").format("/".join(src))) - - contentpath = os.path.join(frappe.local.sites_path, *src) - with open(contentpath) as srcfile: - code = frappe.utils.cstr(srcfile.read()) - - out.append(code) - - return out +get_js = frappe.whitelist()(_get_js) @frappe.whitelist(allow_guest=True) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 31e9fbd1fe..98cadc0098 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -32,7 +32,6 @@ from frappe.utils import ( today, ) from frappe.utils.data import sha256_hash -from frappe.utils.deprecations import deprecated, deprecation_warning from frappe.utils.password import check_password, get_password_reset_limit from frappe.utils.password import update_password as _update_password from frappe.utils.user import get_system_managers @@ -227,9 +226,7 @@ class User(Document): self.roles = [r for r in self.roles if r.role in new_roles] self.append_roles(*new_roles) - @deprecated - def validate_roles(self): - self.populate_role_profile_roles() + from frappe.deprecation_dumpster import validate_roles def move_role_profile_name_to_role_profiles(self): """This handles old role_profile_name field if programatically set. @@ -243,8 +240,12 @@ class User(Document): self.role_profile_name = None return + from frappe.deprecation_dumpster import deprecation_warning + deprecation_warning( - "The field `role_profile_name` is deprecated and will be removed in v16, use `role_profiles` child table instead." + "unknown", + "v16", + "The field `role_profile_name` is deprecated and will be removed in v16, use `role_profiles` child table instead.", ) self.append("role_profiles", {"role_profile": self.role_profile_name}) self.role_profile_name = None @@ -908,12 +909,15 @@ def update_password( @frappe.whitelist(allow_guest=True) def test_password_strength(new_password: str, key=None, old_password=None, user_data: tuple | None = None): - from frappe.utils.deprecations import deprecation_warning from frappe.utils.password_strength import test_password_strength as _test_password_strength if key is not None or old_password is not None: + from frappe.deprecation_dumpster import deprecation_warning + deprecation_warning( - "Arguments `key` and `old_password` are deprecated in function `test_password_strength`." + "unknown", + "v17", + "Arguments `key` and `old_password` are deprecated in function `test_password_strength`.", ) enable_password_policy = frappe.get_system_settings("enable_password_policy") diff --git a/frappe/database/database.py b/frappe/database/database.py index c7ce29de6a..b86c38fe3a 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -32,7 +32,6 @@ from frappe.monitor import get_trace_id from frappe.query_builder.functions import Count from frappe.utils import CallbackManager, cint, get_datetime, get_table_name, getdate, now, sbool from frappe.utils import cast as cast_fieldtype -from frappe.utils.deprecations import deprecated, deprecation_warning if TYPE_CHECKING: from psycopg2 import connection as PostgresConnection @@ -962,8 +961,12 @@ class Database: if dn is None or dt == dn: if not is_single_doctype(dt): return + from frappe.deprecation_dumpster import deprecation_warning + deprecation_warning( - "Calling db.set_value on single doctype is deprecated. This behaviour will be removed in future. Use db.set_single_value instead." + "unknown", + "v17", + "Calling db.set_value on single doctype is deprecated. This behaviour will be removed in future. Use db.set_single_value instead.", ) self.set_single_value( doctype=dt, @@ -1243,10 +1246,9 @@ class Database: # implemented in specific class raise NotImplementedError - @staticmethod - @deprecated - def is_column_missing(e): - return frappe.db.is_missing_column(e) + from frappe.deprecation_dumpster import is_column_missing as _is_column_missing + + is_column_missing = staticmethod(_is_column_missing) def get_descendants(self, doctype, name): """Return descendants of the group node in tree""" diff --git a/frappe/deprecation_dumpster.py b/frappe/deprecation_dumpster.py new file mode 100644 index 0000000000..6887011256 --- /dev/null +++ b/frappe/deprecation_dumpster.py @@ -0,0 +1,290 @@ +""" +Welcome to the Deprecation Dumpster: Where Old Code Goes to Party! 🎉🗑️ + +This file is the final resting place (or should we say, "retirement home"?) for all the deprecated functions and methods of the Frappe framework. It's like a code nursing home, but with more monkey-patching and less bingo. + +Each function or method that checks in here comes with its own personalized decorator, complete with: +1. The date it was marked for deprecation (its "over the hill" birthday) +2. The Frappe version in which it will be removed (its "graduation" to the great codebase in the sky) +3. A user-facing note on alternative solutions (its "parting wisdom") + +Warning: The global namespace herein is more patched up than a sailor's favorite pair of jeans. Proceed with caution and a sense of humor! + +Remember, deprecated doesn't mean useless - it just means these functions are enjoying their golden years before their final bow. Treat them with respect, and maybe bring them some virtual prune juice. + +Enjoy your stay in the Deprecation Dumpster, where every function gets a second chance to shine (or at least, to not break everything). +""" + +import inspect +import os +import sys +import warnings + + +def colorize(text, color_code): + if sys.stdout.isatty(): + return f"\033[{color_code}m{text}\033[0m" + return text + + +class Color: + RED = 91 + YELLOW = 93 + CYAN = 96 + + +try: + # since python 3.13, PEP 702 + from warnings import deprecated as _deprecated +except ImportError: + import functools + import warnings + from collections.abc import Callable + from typing import Optional, TypeVar, Union, overload + + T = TypeVar("T", bound=Callable) + + def _deprecated(message: str, category=DeprecationWarning, stacklevel=1) -> Callable[[T], T]: + def decorator(func: T) -> T: + @functools.wraps(func) + def wrapper(*args, **kwargs): + if message: + warning_msg = f"{func.__name__} is deprecated.\n{message}" + else: + warning_msg = f"{func.__name__} is deprecated." + warnings.warn(warning_msg, category=category, stacklevel=stacklevel + 1) + return func(*args, **kwargs) + + return wrapper + wrapper.__deprecated__ = True # hint for the type checker + + return decorator + + +def deprecated(original: str, marked: str, graduation: str, msg: str, stacklevel: int = 1): + """Decorator to wrap a function/method as deprecated. + + Arguments: + - original: frappe.utils.make_esc (fully qualified) + - marked: 2024-09-13 (the date it has been marked) + - graduation: v17 (generally: current version + 2) + """ + + def decorator(func): + # Get the filename of the caller + frame = inspect.currentframe() + caller_filepath = frame.f_back.f_code.co_filename + if os.path.basename(caller_filepath) != "deprecation_dumpster.py": + raise RuntimeError( + colorize("The deprecated function ", Color.YELLOW) + + colorize(func.__name__, Color.CYAN) + + colorize(" can only be called from ", Color.YELLOW) + + colorize("frappe/deprecation_dumpster.py\n", Color.CYAN) + + colorize("Move the entire function there and import it back via adding\n ", Color.YELLOW) + + colorize(f"from frappe.deprecation_dumpster import {func.__name__}\n", Color.CYAN) + + colorize("to file\n ", Color.YELLOW) + + colorize(caller_filepath, Color.CYAN) + ) + + return functools.wraps(func)( + _deprecated( + colorize(f"`{original}`", Color.CYAN) + + colorize( + f" was moved (DATE: {marked}) to frappe/deprecation_dumpster.py" + f" for removal (from {graduation} onwards); note:\n ", + Color.RED, + ) + + colorize(f"{msg}\n", Color.YELLOW), + stacklevel=stacklevel, + ) + )(func) + + return decorator + + +def deprecation_warning(marked: str, graduation: str, msg: str): + """Warn in-place from a deprecated code path, for objects use `@deprecated` decorator from the deprectation_dumpster" + + Arguments: + - marked: 2024-09-13 (the date it has been marked) + - graduation: v17 (generally: current version + 2) + """ + + warnings.warn( + colorize( + f"This codepath was marked (DATE: {marked}) deprecated" + f" for removal (from {graduation} onwards); note:\n ", + Color.RED, + ) + + colorize(f"{msg}\n", Color.YELLOW), + category=DeprecationWarning, + stacklevel=2, + ) + + +### Party starts here +def _old_deprecated(func): + return deprecated( + "frappe.deprecations.deprecated", + "2024-09-13", + "v17", + "Make use of the frappe/deprecation_dumpster.py file, instead. 🎉🗑️", + )(_deprecated("")(func)) + + +def _old_deprecation_warning(msg): + @deprecated( + "frappe.deprecations.deprecation_warning", + "2024-09-13", + "v17", + "Use frappe.deprecation_dumpster.deprecation_warning, instead. 🎉🗑️", + ) + def deprecation_warning(message, category=DeprecationWarning, stacklevel=1): + warnings.warn(message=message, category=category, stacklevel=stacklevel + 2) + + return deprecation_warning(msg) + + +@deprecated("frappe.utils.make_esc", "unknown", "v17", "Not used anymore.") +def make_esc(esc_chars): + """ + Function generator for Escaping special characters + """ + return lambda s: "".join("\\" + c if c in esc_chars else c for c in s) + + +@deprecated( + "frappe.db.is_column_missing", + "unknown", + "v17", + "Renamed to frappe.db.is_missing_column.", +) +def is_column_missing(e): + import frappe + + return frappe.db.is_missing_column(e) + + +@deprecated( + "frappe.desk.doctype.bulk_update.bulk_update", + "unknown", + "v17", + "Unknown.", +) +def show_progress(docnames, message, i, description): + import frappe + + n = len(docnames) + frappe.publish_progress(float(i) * 100 / n, title=message, description=description) + + +@deprecated( + "frappe.client.get_js", + "unknown", + "v17", + "Unknown.", +) +def get_js(items): + """Load JS code files. Will also append translations + and extend `frappe._messages` + + :param items: JSON list of paths of the js files to be loaded.""" + import json + + import frappe + from frappe import _ + + items = json.loads(items) + out = [] + for src in items: + src = src.strip("/").split("/") + + if ".." in src or src[0] != "assets": + frappe.throw(_("Invalid file path: {0}").format("/".join(src))) + + contentpath = os.path.join(frappe.local.sites_path, *src) + with open(contentpath) as srcfile: + code = frappe.utils.cstr(srcfile.read()) + + out.append(code) + + return out + + +@deprecated( + "frappe.utils.print_format.read_multi_pdf", + "unknown", + "v17", + "Unknown.", +) +def read_multi_pdf(output) -> bytes: + from io import BytesIO + + with BytesIO() as merged_pdf: + output.write(merged_pdf) + return merged_pdf.getvalue() + + +@deprecated("frappe.gzip_compress", "unknown", "v17", "Use py3 methods directly (this was compat for py2).") +def gzip_compress(data, compresslevel=9): + """Compress data in one shot and return the compressed string. + Optional argument is the compression level, in range of 0-9. + """ + import io + from gzip import GzipFile + + buf = io.BytesIO() + with GzipFile(fileobj=buf, mode="wb", compresslevel=compresslevel) as f: + f.write(data) + return buf.getvalue() + + +@deprecated("frappe.gzip_decompress", "unknown", "v17", "Use py3 methods directly (this was compat for py2).") +def gzip_decompress(data): + """Decompress a gzip compressed string in one shot. + Return the decompressed string. + """ + import io + from gzip import GzipFile + + with GzipFile(fileobj=io.BytesIO(data)) as f: + return f.read() + + +@deprecated( + "frappe.email.doctype.email_queue.email_queue.send_mail", + "unknown", + "v17", + "Unknown.", +) +def send_mail(email_queue_name, smtp_server_instance=None): + """This is equivalent to EmailQueue.send. + + This provides a way to make sending mail as a background job. + """ + from frappe.email.doctype.email_queue.email_queue import EmailQueue + + record = EmailQueue.find(email_queue_name) + record.send(smtp_server_instance=smtp_server_instance) + + +@deprecated( + "frappe.geo.country_info.get_translated_dict", + "unknown", + "v17", + "Use frappe.geo.country_info.get_translated_countries, instead.", +) +def get_translated_dict(): + from frappe.geo.country_info import get_translated_countries + + return get_translated_countries() + + +@deprecated( + "User.validate_roles", + "unknown", + "v17", + "Use User.populate_role_profile_roles, instead.", +) +def validate_roles(self): + self.populate_role_profile_roles() diff --git a/frappe/desk/doctype/bulk_update/bulk_update.py b/frappe/desk/doctype/bulk_update/bulk_update.py index a010e50e02..6396357e80 100644 --- a/frappe/desk/doctype/bulk_update/bulk_update.py +++ b/frappe/desk/doctype/bulk_update/bulk_update.py @@ -6,7 +6,6 @@ from frappe import _ from frappe.core.doctype.submission_queue.submission_queue import queue_submission from frappe.model.document import Document from frappe.utils import cint -from frappe.utils.deprecations import deprecated from frappe.utils.scheduler import is_scheduler_inactive @@ -111,7 +110,4 @@ def _bulk_action(doctype, docnames, action, data, task_id=None): return failed -@deprecated -def show_progress(docnames, message, i, description): - n = len(docnames) - frappe.publish_progress(float(i) * 100 / n, title=message, description=description) +from frappe.deprecation_dumpster import show_progress diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py index 4ad59b082c..57ac029d95 100644 --- a/frappe/email/doctype/email_queue/email_queue.py +++ b/frappe/email/doctype/email_queue/email_queue.py @@ -32,7 +32,6 @@ from frappe.utils import ( sbool, split_emails, ) -from frappe.utils.deprecations import deprecated from frappe.utils.verified_command import get_signed_params @@ -223,15 +222,9 @@ class EmailQueue(Document): ).run() -@task(queue="short") -@deprecated -def send_mail(email_queue_name, smtp_server_instance: SMTPServer = None): - """This is equivalent to EmailQueue.send. +from frappe.deprecation_dumpster import send_mail as _send_mail - This provides a way to make sending mail as a background job. - """ - record = EmailQueue.find(email_queue_name) - record.send(smtp_server_instance=smtp_server_instance) +send_mail = task(queue="short")(_send_mail) class SendMailContext: diff --git a/frappe/geo/country_info.py b/frappe/geo/country_info.py index e07581c056..dd2c0eb56a 100644 --- a/frappe/geo/country_info.py +++ b/frappe/geo/country_info.py @@ -8,7 +8,6 @@ import os from functools import lru_cache import frappe -from frappe.utils.deprecations import deprecated from frappe.utils.momentjs import get_all_timezones @@ -39,9 +38,7 @@ def _get_country_timezone_info(): return {"country_info": get_all(), "all_timezones": get_all_timezones()} -@deprecated -def get_translated_dict(): - return get_translated_countries() +from frappe.deprecation_dumpster import get_translated_dict def get_translated_countries(): diff --git a/frappe/handler.py b/frappe/handler.py index d589e33356..4bf496fb82 100644 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -15,7 +15,7 @@ from frappe.core.doctype.server_script.server_script_utils import get_server_scr from frappe.monitor import add_data_to_monitor from frappe.utils import cint from frappe.utils.csvutils import build_csv_response -from frappe.utils.deprecations import deprecated, deprecation_warning +from frappe.utils.deprecations import deprecated from frappe.utils.image import optimize_image from frappe.utils.response import build_response @@ -241,8 +241,12 @@ def get_attr(cmd): if "." in cmd: method = frappe.get_attr(cmd) else: + from frappe.deprecation_dumpster import deprecation_warning + deprecation_warning( - f"Calling shorthand for {cmd} is deprecated, please specify full path in RPC call." + "unknown", + "v17", + f"Calling shorthand for {cmd} is deprecated, please specify full path in RPC call.", ) method = globals()[cmd] return method diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 83a79ead90..a698b5753d 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -23,9 +23,10 @@ from typing import TypedDict from werkzeug.test import Client +from frappe.deprecation_dumpster import gzip_compress, gzip_decompress, make_esc + # utility functions like cint, int, flt, etc. from frappe.utils.data import * -from frappe.utils.deprecations import deprecated from frappe.utils.html_utils import sanitize_html EMAIL_NAME_PATTERN = re.compile(r"[^A-Za-z0-9\u00C0-\u024F\/\_\' ]+") @@ -423,14 +424,6 @@ def get_file_timestamp(fn): return None -# to be deprecated -def make_esc(esc_chars): - """ - Function generator for Escaping special characters - """ - return lambda s: "".join("\\" + c if c in esc_chars else c for c in s) - - # esc / unescape characters -- used for command line def esc(s, esc_chars): """ @@ -888,34 +881,6 @@ def call(fn, *args, **kwargs): return json.loads(frappe.as_json(frappe.call(fn, *args, **kwargs))) -# Following methods are aken as-is from Python 3 codebase -# since gzip.compress and gzip.decompress are not available in Python 2.7 - - -@deprecated -def gzip_compress(data, compresslevel=9): - """Compress data in one shot and return the compressed string. - Optional argument is the compression level, in range of 0-9. - """ - from gzip import GzipFile - - buf = io.BytesIO() - with GzipFile(fileobj=buf, mode="wb", compresslevel=compresslevel) as f: - f.write(data) - return buf.getvalue() - - -@deprecated -def gzip_decompress(data): - """Decompress a gzip compressed string in one shot. - Return the decompressed string. - """ - from gzip import GzipFile - - with GzipFile(fileobj=io.BytesIO(data)) as f: - return f.read() - - def get_safe_filters(filters): try: filters = json.loads(filters) diff --git a/frappe/utils/background_jobs.py b/frappe/utils/background_jobs.py index 89533a9eee..f8a1f12170 100644 --- a/frappe/utils/background_jobs.py +++ b/frappe/utils/background_jobs.py @@ -26,7 +26,6 @@ import frappe.monitor from frappe import _ from frappe.utils import CallbackManager, cint, get_bench_id from frappe.utils.commands import log -from frappe.utils.deprecations import deprecation_warning from frappe.utils.redis_queue import RedisQueue # TTL to keep RQ job logs in redis for. @@ -119,11 +118,19 @@ def enqueue( job_id = create_job_id(job_id) if job_name: - deprecation_warning("Using enqueue with `job_name` is deprecated, use `job_id` instead.") + from frappe.deprecation_dumpster import deprecation_warning + + deprecation_warning( + "unknown", "v17", "Using enqueue with `job_name` is deprecated, use `job_id` instead." + ) if not is_async and not frappe.flags.in_test: + from frappe.deprecation_dumpster import deprecation_warning + deprecation_warning( - "Using enqueue with is_async=False outside of tests is not recommended, use now=True instead." + "unknown", + "v17", + "Using enqueue with is_async=False outside of tests is not recommended, use now=True instead.", ) call_directly = now or (not is_async and not frappe.flags.in_test) diff --git a/frappe/utils/deprecations.py b/frappe/utils/deprecations.py index e98362198b..32b155c1fd 100644 --- a/frappe/utils/deprecations.py +++ b/frappe/utils/deprecations.py @@ -1,27 +1,12 @@ -""" Utils for deprecating functionality in Framework. +"""Utils for deprecating functionality in Framework. WARNING: This file is internal, instead of depending just copy the code or use deprecation libraries. """ -import functools -import warnings - -def deprecated(func): - """Decorator to wrap a function/method as deprecated.""" - - @functools.wraps(func) - def wrapper(*args, **kwargs): - deprecation_warning( - f"{func.__name__} is deprecated and will be removed in next major version.", - stacklevel=1, - ) - return func(*args, **kwargs) - - return wrapper - - -def deprecation_warning(message, category=DeprecationWarning, stacklevel=1): - """like warnings.warn but with auto incremented sane stacklevel.""" - - warnings.warn(message=message, category=category, stacklevel=stacklevel + 2) +from frappe.deprecation_dumpster import ( + _old_deprecated as deprecated, +) +from frappe.deprecation_dumpster import ( + _old_deprecation_warning as deprecation_warning, +) diff --git a/frappe/utils/print_format.py b/frappe/utils/print_format.py index b0267e12ad..55eff4329a 100644 --- a/frappe/utils/print_format.py +++ b/frappe/utils/print_format.py @@ -10,7 +10,6 @@ import frappe from frappe import _ from frappe.core.doctype.access_log.access_log import make_access_log from frappe.translate import print_language -from frappe.utils.deprecations import deprecated from frappe.utils.pdf import get_pdf no_cache = 1 @@ -214,11 +213,7 @@ def _download_multi_pdf( frappe.local.response.type = "pdf" -@deprecated -def read_multi_pdf(output: PdfWriter) -> bytes: - with BytesIO() as merged_pdf: - output.write(merged_pdf) - return merged_pdf.getvalue() +from frappe.deprecation_dumpster import read_multi_pdf @frappe.whitelist(allow_guest=True)