annotationlib --- Chức năng xem xét các chú thích¶
Added in version 3.14.
Source code: Lib/annotationlib.py
Mô-đun annotationlib cung cấp các công cụ để tìm hiểu annotations về các mô-đun, lớp và hàm.
Chú thích là lazily evaluated và thường chứa các tham chiếu chuyển tiếp tới các đối tượng chưa được xác định khi chú thích được tạo. Mô-đun này cung cấp một bộ công cụ cấp thấp có thể được sử dụng để truy xuất các chú thích một cách đáng tin cậy, ngay cả khi có các tham chiếu chuyển tiếp và các trường hợp biên khác.
Mô-đun này hỗ trợ truy xuất chú thích ở ba định dạng chính (xem Format), mỗi định dạng hoạt động tốt nhất cho các trường hợp sử dụng khác nhau:
VALUEđánh giá các chú thích và trả về giá trị của chúng. Cách này dễ thực hiện nhất nhưng có thể gây ra lỗi, ví dụ: nếu chú thích chứa tham chiếu đến tên không xác định.FORWARDREFtrả về đối tượngForwardRefcho các chú thích không thể giải quyết được, cho phép bạn kiểm tra các chú thích mà không cần đánh giá chúng. Điều này hữu ích khi bạn cần làm việc với các chú thích có thể chứa các tham chiếu chuyển tiếp chưa được giải quyết.STRINGtrả về các chú thích dưới dạng một chuỗi, tương tự như cách nó xuất hiện trong tệp nguồn. Điều này hữu ích cho những người tạo tài liệu muốn hiển thị chú thích theo cách dễ đọc.
Hàm get_annotations() là điểm vào chính để truy xuất chú thích. Với một hàm, lớp hoặc mô-đun, nó trả về một từ điển chú thích theo định dạng được yêu cầu. Mô-đun này cũng cung cấp chức năng để làm việc trực tiếp với annotate function được sử dụng để đánh giá các chú thích, chẳng hạn như get_annotate_from_class_namespace() và call_annotate_function(), cũng như chức năng call_evaluate_function() để làm việc với evaluate functions.
Cảnh báo
Hầu hết chức năng trong mô-đun này có thể thực thi mã tùy ý; xem the security section để biết thêm thông tin.
Xem thêm
PEP 649 đã đề xuất mô hình hiện tại về cách hoạt động của các chú thích trong Python.
PEP 749 đã mở rộng trên nhiều khía cạnh khác nhau của PEP 649 và giới thiệu mô-đun annotationlib.
Các phương pháp hay nhất về chú thích cung cấp các phương pháp hay nhất để làm việc với chú thích.
typing-extensions cung cấp một backport của get_annotations() hoạt động trên các phiên bản Python cũ hơn.
Ngữ nghĩa chú thích¶
Cách đánh giá các chú thích đã thay đổi trong lịch sử của Python 3 và hiện vẫn phụ thuộc vào future import. Đã có các mô hình thực thi cho chú thích:
Stock semantics (mặc định trong Python 3.0 đến 3.13; xem PEP 3107 và PEP 526): Các chú thích được đánh giá một cách háo hức vì chúng gặp phải trong mã nguồn.
Stringified annotations (được sử dụng với
from __future__ import annotationstrong Python 3.7 trở lên; xem PEP 563): Chú thích chỉ được lưu trữ dưới dạng chuỗi.Deferred evaluation (mặc định trong Python 3.14 trở lên; xem PEP 649 và PEP 749): Các chú thích chỉ được đánh giá một cách lười biếng, chỉ khi chúng được truy cập.
Ví dụ, hãy xem xét chương trình sau:
def func(a: Cls) -> Không có:
in (a)
lớp Cls: đậu
print(func.__annotations__)
Điều này sẽ hành xử như sau:
Theo ngữ nghĩa gốc (Python 3.13 trở về trước), nó sẽ ném
NameErrorvào dòng nơifuncđược xác định, bởi vìClslà tên không xác định tại thời điểm đó.Dưới các chú thích được xâu chuỗi (nếu sử dụng
from __future__ import annotations), nó sẽ in{'a': 'Cls', 'return': 'None'}.Theo đánh giá trì hoãn (Python 3.14 trở lên), nó sẽ in
{'a': <class 'Cls'>, 'return': None}.
Ngữ nghĩa gốc được sử dụng khi chú thích hàm lần đầu tiên được giới thiệu trong Python 3.0 (bởi PEP 3107) vì đây là cách đơn giản nhất, rõ ràng nhất để triển khai chú thích. Mô hình thực thi tương tự đã được sử dụng khi các chú thích biến được giới thiệu trong Python 3.6 (bởi PEP 526). Tuy nhiên, ngữ nghĩa gốc gây ra vấn đề khi sử dụng chú thích làm gợi ý loại, chẳng hạn như cần tham chiếu đến các tên chưa được xác định khi gặp chú thích. Ngoài ra, còn có vấn đề về hiệu suất khi thực thi chú thích tại thời điểm nhập mô-đun. Do đó, trong Python 3.7, PEP 563 đã giới thiệu khả năng lưu trữ chú thích dưới dạng chuỗi bằng cú pháp from __future__ import annotations. Kế hoạch vào thời điểm đó cuối cùng là đặt hành vi này thành mặc định, nhưng một vấn đề đã xuất hiện: các chú thích được xâu chuỗi khó xử lý hơn đối với những người xem xét kỹ các chú thích trong thời gian chạy. Một đề xuất thay thế, PEP 649, đã giới thiệu mô hình thực thi thứ ba, đánh giá trì hoãn và được triển khai trong Python 3.14. Các chú thích dạng chuỗi vẫn được sử dụng nếu có from __future__ import annotations nhưng hành vi này cuối cùng sẽ bị xóa.
Lớp học¶
- class annotationlib.Format¶
Một
IntEnummô tả các định dạng có thể trả về các chú thích. Các thành viên của enum hoặc các giá trị số nguyên tương đương của chúng có thể được truyền choget_annotations()và các hàm khác trong mô-đun này, cũng như cho các hàm__annotate__.- VALUE = 1¶
Các giá trị là kết quả của việc đánh giá các biểu thức chú thích.
- VALUE_WITH_FAKE_GLOBALS = 2¶
Giá trị đặc biệt được sử dụng để báo hiệu rằng hàm chú thích đang được đánh giá trong một môi trường đặc biệt có toàn cầu giả. Khi được chuyển giá trị này, các hàm chú thích sẽ trả về cùng giá trị như đối với định dạng
Format.VALUEhoặc tăngNotImplementedErrorđể báo hiệu rằng chúng không hỗ trợ thực thi trong môi trường này. Định dạng này chỉ được sử dụng nội bộ và không được chuyển cho các chức năng trong mô-đun này.
- FORWARDREF = 3¶
Giá trị là các giá trị chú thích thực (theo định dạng
Format.VALUE) cho các giá trị được xác định và proxyForwardRefcho các giá trị không xác định. Các đối tượng thực có thể chứa các tham chiếu đến các đối tượng proxyForwardRef.
- STRING = 4¶
Giá trị là chuỗi văn bản của chú thích như nó xuất hiện trong mã nguồn, có thể sửa đổi bao gồm nhưng không giới hạn ở việc chuẩn hóa khoảng trắng và tối ưu hóa giá trị không đổi.
Giá trị chính xác của các chuỗi này có thể thay đổi trong các phiên bản Python trong tương lai.
Added in version 3.14.
- class annotationlib.ForwardRef¶
Một đối tượng proxy để tham chiếu chuyển tiếp trong chú thích.
Các phiên bản của lớp này được trả về khi định dạng
FORWARDREFđược sử dụng và các chú thích chứa tên không thể giải quyết được. Điều này có thể xảy ra khi tham chiếu chuyển tiếp được sử dụng trong chú thích, chẳng hạn như khi một lớp được tham chiếu trước khi nó được xác định.- __forward_arg__¶
Một chuỗi chứa mã được đánh giá để tạo ra
ForwardRef. Chuỗi có thể không tương đương chính xác với nguồn ban đầu.
- evaluate(*, owner=None, globals=None, locals=None, type_params=None, format=Format.VALUE)¶
Đánh giá tham chiếu chuyển tiếp, trả về giá trị của nó.
Nếu đối số format là
VALUE(mặc định), phương thức này có thể đưa ra một ngoại lệ, chẳng hạn nhưNameError, nếu tham chiếu chuyển tiếp đề cập đến một tên không thể giải quyết được. Các đối số của phương thức này có thể được sử dụng để cung cấp các ràng buộc cho các tên mà lẽ ra không được xác định. Nếu đối số format làFORWARDREF, phương thức sẽ không bao giờ đưa ra ngoại lệ nhưng có thể trả về một phiên bảnForwardRef. Ví dụ: nếu đối tượng tham chiếu chuyển tiếp chứa mãlist[undefined], trong đóundefinedlà tên không được xác định thì việc đánh giá nó bằng định dạngFORWARDREFsẽ trả vềlist[ForwardRef('undefined')]. Nếu đối số format làSTRING, phương thức sẽ trả về__forward_arg__.Tham số owner cung cấp cơ chế ưu tiên để truyền thông tin phạm vi cho phương thức này. Chủ sở hữu của
ForwardReflà đối tượng chứa chú thích mà từ đóForwardRefxuất phát, chẳng hạn như đối tượng mô-đun, đối tượng loại hoặc đối tượng hàm.Các tham số globals, locals và type_params cung cấp một cơ chế chính xác hơn để tác động đến các tên có sẵn khi
ForwardRefđược đánh giá. globals và locals được chuyển đếneval(), đại diện cho các không gian tên cục bộ và toàn cầu mà tên được đánh giá. Tham số type_params có liên quan đến các đối tượng được tạo bằng cú pháp gốc cho generic classes và functions. Đó là một bộ type parameters nằm trong phạm vi trong khi tham chiếu chuyển tiếp đang được đánh giá. Ví dụ: nếu đánh giá mộtForwardRefđược truy xuất từ một chú thích được tìm thấy trong không gian tên lớp của một lớp chungC, thì type_params phải được đặt thànhC.__type_params__.Các phiên bản
ForwardRefđượcget_annotations()trả về giữ lại các tham chiếu đến thông tin về phạm vi mà chúng bắt nguồn từ đó, vì vậy, việc gọi phương thức này mà không có đối số nào khác có thể đủ để đánh giá các đối tượng đó. Các phiên bảnForwardRefđược tạo bằng các phương tiện khác có thể không có bất kỳ thông tin nào về phạm vi của chúng, do đó, việc chuyển đối số cho phương thức này có thể cần thiết để đánh giá chúng thành công.Nếu không cung cấp owner, globals, locals hoặc type_params và
ForwardRefkhông chứa thông tin về nguồn gốc của nó, thì từ điển toàn cầu và địa phương trống sẽ được sử dụng.
Added in version 3.14.
Chức năng¶
- annotationlib.annotations_to_string(annotations)¶
Chuyển đổi một lệnh chú thích chứa các giá trị thời gian chạy thành một lệnh chỉ chứa các chuỗi. Nếu các giá trị chưa phải là chuỗi, chúng sẽ được chuyển đổi bằng
type_repr(). Điều này có nghĩa là một trình trợ giúp cho các hàm chú thích do người dùng cung cấp hỗ trợ định dạngSTRINGnhưng không có quyền truy cập vào mã tạo chú thích.Ví dụ: điều này được sử dụng để triển khai
STRINGcho các lớptyping.TypedDictđược tạo thông qua cú pháp hàm:>>> từ cách nhập nhập TypedDict >>> Phim = TypedDict("phim", {"name": str, "year": int}) >>> get_annotations(Phim, định dạng=Format.STRING) {'name': 'str', 'năm': 'int'}
Added in version 3.14.
- annotationlib.call_annotate_function(annotate, format, *, owner=None)¶
Gọi annotate function annotate với format đã cho, một thành viên của
Formatenum và trả về từ điển chú thích do hàm tạo ra.Hàm trợ giúp này là bắt buộc vì các hàm chú thích do trình biên dịch tạo ra cho các hàm, lớp và mô-đun chỉ hỗ trợ định dạng
VALUEkhi được gọi trực tiếp. Để hỗ trợ các định dạng khác, hàm này gọi hàm chú thích trong một môi trường đặc biệt cho phép nó tạo chú thích ở các định dạng khác. Đây là một khối xây dựng hữu ích khi triển khai chức năng cần đánh giá một phần chú thích trong khi một lớp đang được xây dựng.owner là đối tượng sở hữu chức năng chú thích, thường là hàm, lớp hoặc mô-đun. Nếu được cung cấp, nó sẽ được sử dụng ở định dạng
FORWARDREFđể tạo ra đối tượngForwardRefmang nhiều thông tin hơn.Xem thêm
PEP 649 chứa phần giải thích về kỹ thuật triển khai được chức năng này sử dụng.
Added in version 3.14.
- annotationlib.call_evaluate_function(evaluate, format, *, owner=None)¶
Gọi evaluate function evaluate với format đã cho, một thành viên của enum
Formatvà trả về giá trị do hàm tạo ra. Điều này tương tự nhưcall_annotate_function(), nhưng cái sau luôn trả về một chuỗi ánh xạ từ điển tới các chú thích, trong khi hàm này trả về một giá trị duy nhất.Điều này được thiết kế để sử dụng với các hàm đánh giá được tạo cho các phần tử được đánh giá lười biếng liên quan đến bí danh loại và tham số loại:
typing.TypeAliasType.evaluate_value(), giá trị của bí danh loạityping.TypeVar.evaluate_bound(), giới hạn của các biến loạityping.TypeVar.evaluate_constraints(), các ràng buộc của biến loạityping.TypeVar.evaluate_default(), giá trị mặc định của biến loạityping.ParamSpec.evaluate_default(), giá trị mặc định của thông số kỹ thuậttyping.TypeVarTuple.evaluate_default(), giá trị mặc định của bộ biến kiểu
owner là đối tượng sở hữu hàm đánh giá, chẳng hạn như bí danh loại hoặc đối tượng biến loại.
format có thể được sử dụng để kiểm soát định dạng trả về giá trị:
>>> gõ Bí danh = không xác định >>> call_evaluate_function(Bí danh.evaluate_value, Format.VALUE) Traceback (cuộc gọi gần đây nhất): ... NameError: tên 'không xác định' không được xác định >>> call_evaluate_function(Bí danh.evaluate_value, Format.FORWARDREF) ForwardRef('không xác định') >>> call_evaluate_function(Bí danh.evaluate_value, Format.STRING) 'không xác định'
Added in version 3.14.
- annotationlib.get_annotate_from_class_namespace(namespace)¶
Truy xuất annotate function từ từ điển không gian tên lớp namespace. Trả về
Nonenếu không gian tên không chứa chức năng chú thích. Điều này chủ yếu hữu ích trước khi lớp được tạo hoàn chỉnh (ví dụ: trong siêu dữ liệu); sau khi lớp tồn tại, chức năng chú thích có thể được truy xuất bằngcls.__annotate__. Xem below để biết ví dụ sử dụng hàm này trong siêu dữ liệu.Added in version 3.14.
- annotationlib.get_annotations(obj, *, globals=None, locals=None, eval_str=False, format=Format.VALUE)¶
Tính toán các chú thích cho một đối tượng.
obj có thể là một đối tượng có thể gọi, lớp, mô-đun hoặc đối tượng khác có thuộc tính
__annotate__hoặc__annotations__. Vượt qua bất kỳ đối tượng nào khác sẽ tăngTypeError.Tham số format kiểm soát định dạng trả về các chú thích và phải là thành viên của enum
Formathoặc số nguyên tương đương của nó. Các định dạng khác nhau hoạt động như sau:VALUE:
object.__annotations__được thử trước; nếu điều đó không tồn tại, hàmobject.__annotate__sẽ được gọi nếu nó tồn tại.FORWARDREF: Nếu
object.__annotations__tồn tại và có thể được đánh giá thành công thì nó sẽ được sử dụng; nếu không, hàmobject.__annotate__sẽ được gọi. Nếu nó cũng không tồn tại,object.__annotations__sẽ được thử lại và mọi lỗi khi truy cập nó sẽ được khắc phục lại.Khi gọi
object.__annotate__, đầu tiên nó được gọi bằngFORWARDREF. Nếu điều này không được triển khai, thì nó sẽ kiểm tra xemVALUE_WITH_FAKE_GLOBALScó được hỗ trợ hay không và sử dụng nó trong môi trường toàn cầu giả. Nếu cả hai định dạng này đều không được hỗ trợ, nó sẽ quay lại sử dụngVALUE. NếuVALUEkhông thành công, lỗi từ lệnh gọi này sẽ xuất hiện.
STRING: Nếu
object.__annotate__tồn tại, nó sẽ được gọi đầu tiên; mặt khác,object.__annotations__được sử dụng và xâu chuỗi bằngannotations_to_string().Khi gọi
object.__annotate__, đầu tiên nó được gọi bằngSTRING. Nếu điều này không được triển khai, thì nó sẽ kiểm tra xemVALUE_WITH_FAKE_GLOBALScó được hỗ trợ hay không và sử dụng nó trong môi trường toàn cầu giả. Nếu cả hai định dạng này đều không được hỗ trợ, nó sẽ quay lại sử dụngVALUEvới kết quả được chuyển đổi bằngannotations_to_string(). NếuVALUEkhông thành công, lỗi từ lệnh gọi này sẽ xuất hiện.
Trả về một lệnh.
get_annotations()trả về một lệnh mới mỗi khi nó được gọi; gọi nó hai lần trên cùng một đối tượng sẽ trả về hai ký tự khác nhau nhưng tương đương.Hàm này xử lý một số chi tiết cho bạn:
Nếu eval_str là đúng, các giá trị thuộc loại
strsẽ không được xâu chuỗi bằngeval(). Điều này được thiết kế để sử dụng với các chú thích được xâu chuỗi (from __future__ import annotations). Sẽ có lỗi khi đặt eval_str thành true với các định dạng khácFormat.VALUE.Nếu obj không có lệnh chú thích, sẽ trả về một lệnh trống. (Các hàm và phương thức luôn có một chú thích; các lớp, mô-đun và các loại lệnh gọi khác có thể không có.)
Bỏ qua các chú thích được kế thừa trên các lớp cũng như các chú thích trên siêu dữ liệu. Nếu một lớp không có lệnh chú thích riêng, sẽ trả về một lệnh trống.
Tất cả quyền truy cập vào các thành viên đối tượng và giá trị dict đều được thực hiện bằng
getattr()vàdict.get()để đảm bảo an toàn.
eval_str kiểm soát xem các giá trị của loại
strcó được thay thế bằng kết quả của việc gọieval()trên các giá trị đó hay không:Nếu eval_str là đúng,
eval()được gọi trên các giá trị thuộc loạistr. (Lưu ý rằngget_annotations()không bắt được ngoại lệ; nếueval()đưa ra một ngoại lệ, nó sẽ giải phóng ngăn xếp vượt qua lệnh gọiget_annotations().)Nếu eval_str là sai (mặc định), các giá trị của loại
strsẽ không thay đổi.
globals và locals được chuyển vào
eval(); xem tài liệu vềeval()để biết thêm thông tin. Nếu globals hoặc locals làNone, hàm này có thể thay thế giá trị đó bằng giá trị mặc định theo ngữ cảnh cụ thể, tùy thuộc vàotype(obj):Nếu obj là một mô-đun, globals mặc định là
obj.__dict__.Nếu obj là một lớp, globals mặc định là
sys.modules[obj.__module__].__dict__và locals mặc định là không gian tên lớp obj.Nếu obj là một hàm có thể gọi được thì globals sẽ mặc định là
obj.__globals__, mặc dù nếu obj là một hàm được gói (sử dụngfunctools.update_wrapper()) hoặc một đối tượngfunctools.partial, thì nó sẽ được mở gói cho đến khi tìm thấy một hàm không được gói.
Gọi
get_annotations()là cách tốt nhất để truy cập vào chú thích của bất kỳ đối tượng nào. Xem Các phương pháp hay nhất về chú thích để biết thêm thông tin về các phương pháp hay nhất về chú thích.>>> def f(a: int, b: str) -> float: ... vượt qua >>> get_annotations(f) {'a': <class 'int'>, 'b': <class 'str'>, 'return': <class 'float'>}
Added in version 3.14.
- annotationlib.type_repr(value)¶
Chuyển đổi giá trị Python tùy ý sang định dạng phù hợp để sử dụng theo định dạng
STRING. Điều này gọirepr()cho hầu hết các đối tượng, nhưng có cách xử lý đặc biệt đối với một số đối tượng, chẳng hạn như đối tượng loại.Điều này có nghĩa là một trình trợ giúp cho các hàm chú thích do người dùng cung cấp hỗ trợ định dạng
STRINGnhưng không có quyền truy cập vào mã tạo chú thích. Nó cũng có thể được sử dụng để cung cấp cách biểu diễn chuỗi thân thiện với người dùng cho các đối tượng khác có chứa các giá trị thường gặp trong chú thích.Added in version 3.14.
Công thức nấu ăn¶
Sử dụng chú thích trong siêu dữ liệu¶
Zz000zz có thể muốn kiểm tra hoặc thậm chí sửa đổi các chú thích trong nội dung lớp trong quá trình tạo lớp. Làm như vậy yêu cầu truy xuất các chú thích từ từ điển vùng tên lớp. Đối với các lớp được tạo bằng from __future__ import annotations, các chú thích sẽ nằm trong khóa __annotations__ của từ điển. Đối với các lớp khác có chú thích, get_annotate_from_class_namespace() có thể được sử dụng để lấy hàm chú thích và call_annotate_function() có thể được sử dụng để gọi và truy xuất chú thích. Sử dụng định dạng FORWARDREF thường sẽ là tốt nhất, vì điều này cho phép các chú thích tham chiếu đến những cái tên chưa thể giải quyết được khi lớp được tạo.
Để sửa đổi chú thích, cách tốt nhất là tạo hàm chú thích trình bao bọc gọi hàm chú thích ban đầu, thực hiện mọi điều chỉnh cần thiết và trả về kết quả.
Dưới đây là ví dụ về siêu dữ liệu lọc tất cả các chú thích typing.ClassVar khỏi lớp và đặt chúng vào một thuộc tính riêng biệt:
nhập chú thíchlib
nhập gõ
lớp ClassVarSeparator(loại):
def __new__(mcls, tên, căn cứ, ns):
nếu "__annotations__" trong ns: # from __future__ nhập chú thích
chú thích = ns["__annotations__"]
classvar_keys = {
khóa cho khóa, giá trị trong chú thích.items()
so sánh chuỗi # Use để đơn giản; một giải pháp mạnh mẽ hơn
# could sử dụng chú thíchlib.ForwardRef.evaluate
if value.startswith("ClassVar")
}
classvars = {key: chú thích[key] cho khóa trong classvar_keys}
ns["__annotations__"] = {
key: giá trị cho khóa, giá trị trong chú thích.items()
nếu khóa không có trong classvar_keys
}
bọc_annotate = Không có
elif chú thích := chú thíchlib.get_annotate_from_class_namespace(ns):
chú thích = chú thíchlib.call_annotate_function(
chú thích, định dạng=annotationlib.Format.FORWARDREF
)
classvar_keys = {
khóa cho khóa, giá trị trong chú thích.items()
nếu đang gõ.get_origin(value) đang gõ.ClassVar
}
classvars = {key: chú thích[key] cho khóa trong classvar_keys}
def được bọc_annotate (định dạng):
annos = chú thíchlib.call_annotate_function(chú thích, định dạng, chủ sở hữu=typ)
trả về {key: giá trị cho khóa, giá trị trong annos.items() nếu khóa không có trong classvar_keys}
khác: chú thích # no
lớpvars = {}
bọc_annotate = Không có
typ = super().__new__(mcls, tên, căn cứ, ns)
nếu Wrap_annotate không phải là Không có:
# Wrap __annotate__ ban đầu có trình bao bọc loại bỏ ClassVars
typ.__annotate__ = bọc_annotate
typ.classvars = classvars # Store các ClassVars trong một thuộc tính riêng biệt
kiểu trả về
Hạn chế của định dạng STRING¶
Định dạng STRING nhằm mục đích gần đúng mã nguồn của chú thích, nhưng chiến lược triển khai được sử dụng có nghĩa là không phải lúc nào cũng có thể khôi phục mã nguồn chính xác.
Đầu tiên, trình xâu chuỗi tất nhiên không thể khôi phục bất kỳ thông tin nào không có trong mã được biên dịch, bao gồm các nhận xét, khoảng trắng, dấu ngoặc đơn và các thao tác được trình biên dịch đơn giản hóa.
Thứ hai, trình tạo chuỗi có thể chặn hầu hết tất cả các hoạt động liên quan đến tên được tra cứu trong một phạm vi nào đó, nhưng nó không thể chặn các hoạt động hoạt động hoàn toàn trên các hằng số. Hệ quả tất yếu là điều này cũng có nghĩa là sẽ không an toàn khi yêu cầu định dạng STRING trên mã không đáng tin cậy: Python đủ mạnh để có thể thực thi mã tùy ý ngay cả khi không có quyền truy cập vào bất kỳ toàn cục hoặc nội dung nào. Ví dụ:
>>> def f(x: (1).__class__.__base__.__subclasses__()[-1].__init__.__buildins__["print"]("Xin chào thế giới")): vượt qua
...
>>> chú thíchlib.get_annotations(f, format=annotationlib.Format.STRING)
Xin chào thế giới
{'x': 'Không có'}
Ghi chú
Ví dụ cụ thể này hoạt động tại thời điểm viết bài, nhưng nó phụ thuộc vào chi tiết triển khai và không được đảm bảo sẽ hoạt động trong tương lai.
Trong số các loại biểu thức khác nhau tồn tại trong Python, được biểu thị bằng mô-đun ast, một số biểu thức được hỗ trợ, nghĩa là định dạng STRING thường có thể khôi phục mã nguồn gốc; những cái khác không được hỗ trợ, có nghĩa là chúng có thể dẫn đến kết quả đầu ra không chính xác hoặc bị lỗi.
Những điều sau đây được hỗ trợ (đôi khi có cảnh báo):
-
ast.Invert(~),ast.UAdd(+) vàast.USub(-) được hỗ trợast.Not(not) không được hỗ trợ
ast.Dict(trừ khi sử dụng**giải nén)ast.Call(trừ khi sử dụng**giải nén)ast.Constant(mặc dù không phải là biểu diễn chính xác của hằng số; ví dụ: chuỗi thoát trong chuỗi bị mất; số thập lục phân được chuyển thành số thập phân)ast.Attribute(giả sử giá trị không phải là hằng số)ast.Subscript(giả sử giá trị không phải là hằng số)ast.Starred(giải nén*)
Các mục sau đây không được hỗ trợ nhưng sẽ đưa ra lỗi thông tin khi trình tạo chuỗi gặp phải:
ast.FormattedValue(chuỗi f; không phát hiện được lỗi nếu sử dụng các công cụ xác định chuyển đổi như!r)ast.JoinedStr(dây f)
Các mục sau không được hỗ trợ và dẫn đến kết quả đầu ra không chính xác:
Những điều sau đây không được phép trong phạm vi chú thích và do đó không liên quan:
Hạn chế của định dạng FORWARDREF¶
Định dạng FORWARDREF nhằm mục đích tạo ra nhiều giá trị thực nhất có thể, với mọi thứ không thể giải quyết được sẽ được thay thế bằng đối tượng ForwardRef. Nó bị ảnh hưởng bởi các Hạn chế tương tự như định dạng STRING: các chú thích thực hiện các thao tác trên chữ hoặc sử dụng các loại biểu thức không được hỗ trợ có thể đưa ra các ngoại lệ khi được đánh giá bằng định dạng FORWARDREF.
Dưới đây là một số ví dụ về hành vi với các biểu thức không được hỗ trợ:
>>> từ nhập chú thíchlib get_annotations, Định dạng
>>> def zerodiv(x: 1 / 0): ...
>>> get_annotations(zerodiv, format=Format.STRING)
Traceback (cuộc gọi gần đây nhất):
...
ZeroDivisionError: chia cho 0
>>> get_annotations(zerodiv, format=Format.FORWARDREF)
Traceback (cuộc gọi gần đây nhất):
...
ZeroDivisionError: chia cho 0
>>> def ifexp(x: 1 if y else 0): ...
>>> get_annotations(ifexp, format=Format.STRING)
{'x': '1'}
Ý nghĩa bảo mật của các chú thích nội tâm¶
Phần lớn chức năng trong mô-đun này liên quan đến việc thực thi mã liên quan đến chú thích, sau đó có thể thực hiện những việc tùy ý. Ví dụ: get_annotations() có thể gọi annotate function tùy ý và ForwardRef.evaluate() có thể gọi eval() trên một chuỗi tùy ý. Mã chứa trong chú thích có thể thực hiện các lệnh gọi hệ thống tùy ý, nhập vòng lặp vô hạn hoặc thực hiện bất kỳ thao tác nào khác. Điều này cũng đúng đối với mọi quyền truy cập vào thuộc tính __annotations__ và đối với các chức năng khác nhau trong mô-đun typing hoạt động với các chú thích, chẳng hạn như typing.get_type_hints().
Bất kỳ vấn đề bảo mật nào phát sinh từ việc này cũng áp dụng ngay sau khi nhập mã có thể chứa các chú thích không đáng tin cậy: việc nhập mã luôn có thể khiến các hoạt động tùy ý được thực hiện. Tuy nhiên, sẽ không an toàn khi chấp nhận chuỗi hoặc dữ liệu đầu vào khác từ nguồn không đáng tin cậy và chuyển chúng tới bất kỳ API nào để xem xét chú thích, chẳng hạn như bằng cách chỉnh sửa từ điển __annotations__ hoặc trực tiếp tạo đối tượng ForwardRef.