diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2025-01-08 16:55:48 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2025-01-08 17:08:25 +0300 |
commit | 9b668f4d12c7d6fc709ffc4ab3b87a7e8aef8e2f (patch) | |
tree | 2795579b13b11329bc8f9dc82b48d04a04023616 | |
parent | 5487449624c89f51c526f9e6de269590a273ebec (diff) | |
download | ydb-9b668f4d12c7d6fc709ffc4ab3b87a7e8aef8e2f.tar.gz |
Intermediate changes
commit_hash:f15cd769f87762d4807f0d4ae628b5dfb830bef5
42 files changed, 2203 insertions, 702 deletions
diff --git a/contrib/libs/libpng/.yandex_meta/__init__.py b/contrib/libs/libpng/.yandex_meta/__init__.py index 47a1b39885..0e0556b959 100644 --- a/contrib/libs/libpng/.yandex_meta/__init__.py +++ b/contrib/libs/libpng/.yandex_meta/__init__.py @@ -7,6 +7,9 @@ from devtools.yamaker.project import GNUMakeNixProject def post_install(self): with self.yamakes["."] as libpng: + # libpng generates export script, but we are going to link statically + os.remove(f"{self.dstdir}/libpng.vers") + # libpng generates config.h but does not use any of its defines. os.remove(f"{self.dstdir}/config.h") libpng.CFLAGS.remove("-DHAVE_CONFIG_H") diff --git a/contrib/libs/libpng/.yandex_meta/devtools.copyrights.report b/contrib/libs/libpng/.yandex_meta/devtools.copyrights.report index 0383d68e36..943f1a9a9d 100644 --- a/contrib/libs/libpng/.yandex_meta/devtools.copyrights.report +++ b/contrib/libs/libpng/.yandex_meta/devtools.copyrights.report @@ -151,30 +151,6 @@ BELONGS ya.make powerpc/filter_vsx_intrinsics.c [3:5] powerpc/powerpc_init.c [4:6] -KEEP COPYRIGHT_SERVICE_LABEL 45a151e61f5d9f2924b225327a93f376 -BELONGS ya.make - License text: - Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2023 Max Stepin, - and are delimited by "#ifdef PNG_APNG_SUPPORTED / #endif" directives - surrounding them in the modified libpng source files. - Scancode info: - Original SPDX id: COPYRIGHT_SERVICE_LABEL - Score : 100.00 - Match type : COPYRIGHT - Files with this license: - LICENSE [6:8] - png.h [29:31] - -KEEP COPYRIGHT_SERVICE_LABEL 4644b73a371ed2c05c5ac0b3899bd563 -BELONGS ya.make - Note: matched license text is too long. Read it in the source files. - Scancode info: - Original SPDX id: COPYRIGHT_SERVICE_LABEL - Score : 100.00 - Match type : COPYRIGHT - Files with this license: - png.c [819:827] - KEEP COPYRIGHT_SERVICE_LABEL 4ab05cc7c9d27c8a3fbcc6350cf56230 BELONGS ya.make License text: @@ -212,7 +188,7 @@ BELONGS ya.make Files with this license: LICENSE [13:17] png.c [4:7] - png.c [819:827] + png.c [819:825] png.h [6:9] png.h [36:40] pngget.c [4:7] @@ -238,7 +214,7 @@ BELONGS ya.make KEEP COPYRIGHT_SERVICE_LABEL 5fc45730df09b1e4ad9f9d6a2d097232 BELONGS ya.make License text: - Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2023 Max Stepin, + Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2024 Max Stepin, and are delimited by "#ifdef PNG_APNG_SUPPORTED / #endif" directives surrounding them in the modified libpng source files. Scancode info: @@ -260,7 +236,7 @@ BELONGS ya.make LICENSE [13:17] LICENSE [87:91] png.c [4:7] - png.c [819:827] + png.c [819:825] png.h [6:9] png.h [36:40] png.h [110:114] @@ -293,7 +269,7 @@ BELONGS ya.make Match type : COPYRIGHT Files with this license: png.c [4:7] - png.c [819:827] + png.c [819:825] png.h [6:9] pngget.c [4:7] pnglibconf.h [5:6] @@ -307,6 +283,20 @@ BELONGS ya.make pngwrite.c [4:7] pngwutil.c [4:7] +KEEP COPYRIGHT_SERVICE_LABEL 9524c4c0aaaea5b1ba2ffbc048713d9e +BELONGS ya.make + License text: + Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2024 Max Stepin, + and are delimited by "#ifdef PNG_APNG_SUPPORTED / #endif" directives + surrounding them in the modified libpng source files. + Scancode info: + Original SPDX id: COPYRIGHT_SERVICE_LABEL + Score : 100.00 + Match type : COPYRIGHT + Files with this license: + LICENSE [6:8] + png.h [29:31] + KEEP COPYRIGHT_SERVICE_LABEL a590c93284482861546eac4b62974abf BELONGS ya.make License text: @@ -331,7 +321,7 @@ BELONGS ya.make LICENSE [13:17] LICENSE [103:104] png.c [4:7] - png.c [819:827] + png.c [819:825] png.h [6:9] png.h [36:40] png.h [126:127] @@ -381,16 +371,6 @@ BELONGS ya.make Files with this license: pngset.c [4:7] -KEEP COPYRIGHT_SERVICE_LABEL ba5441522ec42a475b4efc4c19d96da2 -BELONGS ya.make - Note: matched license text is too long. Read it in the source files. - Scancode info: - Original SPDX id: COPYRIGHT_SERVICE_LABEL - Score : 100.00 - Match type : COPYRIGHT - Files with this license: - png.c [819:827] - KEEP COPYRIGHT_SERVICE_LABEL c486f222c872d0101647b08aaad5c74c BELONGS ya.make Note: matched license text is too long. Read it in the source files. diff --git a/contrib/libs/libpng/.yandex_meta/devtools.licenses.report b/contrib/libs/libpng/.yandex_meta/devtools.licenses.report index ffa339d7d3..90b3c5d0b6 100644 --- a/contrib/libs/libpng/.yandex_meta/devtools.licenses.report +++ b/contrib/libs/libpng/.yandex_meta/devtools.licenses.report @@ -218,18 +218,6 @@ BELONGS ya.make Files with this license: CHANGES [2692:2692] -KEEP libpng-2.0 70f7ac29617c1f96c3c72a8706953d6f -BELONGS ya.make -FILE_INCLUDE LICENSE found in files: LICENSE at line 1 - Note: matched license text is too long. Read it in the source files. - Scancode info: - Original SPDX id: libpng-2.0 - Score : 95.76 - Match type : TEXT - Links : http://www.libpng.org/pub/png/src/libpng-LICENSE.txt, https://spdx.org/licenses/libpng-2.0 - Files with this license: - LICENSE [1:42] - SKIP LicenseRef-scancode-free-unknown 7223032bb9df755f6ce7298a13e95cee BELONGS ya.make # we have no contrib in libpng directory @@ -422,6 +410,18 @@ BELONGS ya.make Files with this license: png.h [73:94] +KEEP libpng-2.0 d964429a132bf678731f5df030520bfb +BELONGS ya.make +FILE_INCLUDE LICENSE found in files: LICENSE at line 1 + Note: matched license text is too long. Read it in the source files. + Scancode info: + Original SPDX id: libpng-2.0 + Score : 95.76 + Match type : TEXT + Links : http://www.libpng.org/pub/png/src/libpng-LICENSE.txt, https://spdx.org/licenses/libpng-2.0 + Files with this license: + LICENSE [1:42] + KEEP Libpng e9bb4f8fbab91484ba62ccb1611b70bb BELONGS ya.make License text: diff --git a/contrib/libs/libpng/.yandex_meta/licenses.list.txt b/contrib/libs/libpng/.yandex_meta/licenses.list.txt index 40d116cc42..80ff5f75ee 100644 --- a/contrib/libs/libpng/.yandex_meta/licenses.list.txt +++ b/contrib/libs/libpng/.yandex_meta/licenses.list.txt @@ -1,16 +1,4 @@ ====================COPYRIGHT==================== - "Copyright (c) 2018-2023 Cosmin Truta" PNG_STRING_NEWLINE \ - "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \ - PNG_STRING_NEWLINE \ - "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ - "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ - PNG_STRING_NEWLINE \ - "Portions Copyright (c) 2006-2007 Andrew Smith" PNG_STRING_NEWLINE \ - "Portions Copyright (c) 2008-2023 Max Stepin" PNG_STRING_NEWLINE ; -#endif - - -====================COPYRIGHT==================== * Copyright (c) 1995-2023 The PNG Reference Library Authors. * Copyright (c) 2018-2023 Cosmin Truta. * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. @@ -118,7 +106,7 @@ ====================COPYRIGHT==================== -Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2023 Max Stepin, +Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2024 Max Stepin, and are delimited by "#ifdef PNG_APNG_SUPPORTED / #endif" directives surrounding them in the modified libpng source files. @@ -611,7 +599,7 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE This modified version of libpng code adds animated PNG support and is released under the libpng license described below. The modifications are -Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2023 Max Stepin, +Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2024 Max Stepin, and are delimited by "#ifdef PNG_APNG_SUPPORTED / #endif" directives surrounding them in the modified libpng source files. @@ -1065,7 +1053,7 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE This modified version of libpng code adds animated PNG support and is released under the libpng license described below. The modifications are -Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2023 Max Stepin, +Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2024 Max Stepin, and are delimited by "#ifdef PNG_APNG_SUPPORTED / #endif" directives surrounding them in the modified libpng source files. diff --git a/contrib/libs/libpng/.yandex_meta/override.nix b/contrib/libs/libpng/.yandex_meta/override.nix index 61a324e710..23f741e884 100644 --- a/contrib/libs/libpng/.yandex_meta/override.nix +++ b/contrib/libs/libpng/.yandex_meta/override.nix @@ -8,17 +8,12 @@ pkgs: attrs: with pkgs; rec { hash = "sha256-Rad7Y5Z9PUCipBTQcB7LEP8fIVTG3JsnMeknUkZ/rRg="; }; - # nixpkgs use a patch from libpng-apng project for getting A(nimated) PNG support. - # While libpng-apng project patch is functionally equivalent to apng one, - # the latter seems to provide somewhat better code. - # - # The sha256 checksum of the patch has to be updated upon libpng version update. - patch_src = fetchurl { - url = "mirror://sourceforge/apng/libpng-${version}-apng.patch.gz"; - hash = "sha256-esYjxN5hBg8Uue6AOAowulcB22U7rnQz2TjLM0+w+0w="; - }; + # nixpkgs applies apng patch from sourceforge.net, which changes for every libpng version. + # We apply a sligthly modified version of this patch via patches/apng.patch + patches = []; + postPatch = ""; - postPatch = '' - gunzip < ${patch_src} | patch -Np0 - ''; + configureFlags = [ + "--build=x86_64-pc-linux-gnu" + ]; } diff --git a/contrib/libs/libpng/LICENSE b/contrib/libs/libpng/LICENSE index 8e4d6a4436..10033488bd 100644 --- a/contrib/libs/libpng/LICENSE +++ b/contrib/libs/libpng/LICENSE @@ -3,7 +3,7 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE This modified version of libpng code adds animated PNG support and is released under the libpng license described below. The modifications are -Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2023 Max Stepin, +Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2024 Max Stepin, and are delimited by "#ifdef PNG_APNG_SUPPORTED / #endif" directives surrounding them in the modified libpng source files. diff --git a/contrib/libs/libpng/libpng.vers b/contrib/libs/libpng/libpng.vers deleted file mode 100644 index 5762bcee46..0000000000 --- a/contrib/libs/libpng/libpng.vers +++ /dev/null @@ -1,268 +0,0 @@ -PNG16_0 {global: - png_access_version_number; - png_set_sig_bytes; - png_sig_cmp; - png_create_read_struct; - png_create_write_struct; - png_get_compression_buffer_size; - png_set_compression_buffer_size; - png_set_longjmp_fn; - png_longjmp; - png_reset_zstream; - png_create_read_struct_2; - png_create_write_struct_2; - png_write_sig; - png_write_chunk; - png_write_chunk_start; - png_write_chunk_data; - png_write_chunk_end; - png_create_info_struct; - png_info_init_3; - png_write_info_before_PLTE; - png_write_info; - png_read_info; - png_convert_to_rfc1123; - png_convert_to_rfc1123_buffer; - png_convert_from_struct_tm; - png_convert_from_time_t; - png_set_expand; - png_set_expand_gray_1_2_4_to_8; - png_set_palette_to_rgb; - png_set_tRNS_to_alpha; - png_set_expand_16; - png_set_bgr; - png_set_gray_to_rgb; - png_set_rgb_to_gray; - png_set_rgb_to_gray_fixed; - png_get_rgb_to_gray_status; - png_build_grayscale_palette; - png_set_alpha_mode; - png_set_alpha_mode_fixed; - png_set_strip_alpha; - png_set_swap_alpha; - png_set_invert_alpha; - png_set_filler; - png_set_add_alpha; - png_set_swap; - png_set_packing; - png_set_packswap; - png_set_shift; - png_set_interlace_handling; - png_set_invert_mono; - png_set_background; - png_set_background_fixed; - png_set_scale_16; - png_set_strip_16; - png_set_quantize; - png_set_gamma; - png_set_gamma_fixed; - png_set_flush; - png_write_flush; - png_start_read_image; - png_read_update_info; - png_read_rows; - png_read_row; - png_read_image; - png_write_row; - png_write_rows; - png_write_image; - png_write_end; - png_read_end; - png_destroy_info_struct; - png_destroy_read_struct; - png_destroy_write_struct; - png_set_crc_action; - png_set_filter; - png_set_filter_heuristics; - png_set_filter_heuristics_fixed; - png_set_compression_level; - png_set_compression_mem_level; - png_set_compression_strategy; - png_set_compression_window_bits; - png_set_compression_method; - png_set_text_compression_level; - png_set_text_compression_mem_level; - png_set_text_compression_strategy; - png_set_text_compression_window_bits; - png_set_text_compression_method; - png_init_io; - png_set_error_fn; - png_get_error_ptr; - png_set_write_fn; - png_set_read_fn; - png_get_io_ptr; - png_set_read_status_fn; - png_set_write_status_fn; - png_set_mem_fn; - png_get_mem_ptr; - png_set_read_user_transform_fn; - png_set_write_user_transform_fn; - png_set_user_transform_info; - png_get_user_transform_ptr; - png_get_current_row_number; - png_get_current_pass_number; - png_set_read_user_chunk_fn; - png_get_user_chunk_ptr; - png_set_progressive_read_fn; - png_get_progressive_ptr; - png_process_data; - png_process_data_pause; - png_process_data_skip; - png_progressive_combine_row; - png_malloc; - png_calloc; - png_malloc_warn; - png_free; - png_free_data; - png_data_freer; - png_malloc_default; - png_free_default; - png_error; - png_chunk_error; - png_warning; - png_chunk_warning; - png_benign_error; - png_chunk_benign_error; - png_set_benign_errors; - png_get_valid; - png_get_rowbytes; - png_get_rows; - png_set_rows; - png_get_channels; - png_get_image_width; - png_get_image_height; - png_get_bit_depth; - png_get_color_type; - png_get_filter_type; - png_get_interlace_type; - png_get_compression_type; - png_get_pixels_per_meter; - png_get_x_pixels_per_meter; - png_get_y_pixels_per_meter; - png_get_pixel_aspect_ratio; - png_get_pixel_aspect_ratio_fixed; - png_get_x_offset_pixels; - png_get_y_offset_pixels; - png_get_x_offset_microns; - png_get_y_offset_microns; - png_get_signature; - png_get_bKGD; - png_set_bKGD; - png_get_cHRM; - png_get_cHRM_XYZ; - png_get_cHRM_fixed; - png_get_cHRM_XYZ_fixed; - png_set_cHRM; - png_set_cHRM_XYZ; - png_set_cHRM_fixed; - png_set_cHRM_XYZ_fixed; - png_get_eXIf; - png_set_eXIf; - png_get_eXIf_1; - png_set_eXIf_1; - png_get_gAMA; - png_get_gAMA_fixed; - png_set_gAMA; - png_set_gAMA_fixed; - png_get_hIST; - png_set_hIST; - png_get_IHDR; - png_set_IHDR; - png_get_oFFs; - png_set_oFFs; - png_get_pCAL; - png_set_pCAL; - png_get_pHYs; - png_set_pHYs; - png_get_PLTE; - png_set_PLTE; - png_get_sBIT; - png_set_sBIT; - png_get_sRGB; - png_set_sRGB; - png_set_sRGB_gAMA_and_cHRM; - png_get_iCCP; - png_set_iCCP; - png_get_sPLT; - png_set_sPLT; - png_get_text; - png_set_text; - png_get_tIME; - png_set_tIME; - png_get_tRNS; - png_set_tRNS; - png_get_sCAL; - png_get_sCAL_fixed; - png_get_sCAL_s; - png_set_sCAL; - png_set_sCAL_fixed; - png_set_sCAL_s; - png_set_keep_unknown_chunks; - png_handle_as_unknown; - png_set_unknown_chunks; - png_set_unknown_chunk_location; - png_get_unknown_chunks; - png_set_invalid; - png_read_png; - png_write_png; - png_get_copyright; - png_get_header_ver; - png_get_header_version; - png_get_libpng_ver; - png_permit_mng_features; - png_set_user_limits; - png_get_user_width_max; - png_get_user_height_max; - png_set_chunk_cache_max; - png_get_chunk_cache_max; - png_set_chunk_malloc_max; - png_get_chunk_malloc_max; - png_get_pixels_per_inch; - png_get_x_pixels_per_inch; - png_get_y_pixels_per_inch; - png_get_x_offset_inches; - png_get_x_offset_inches_fixed; - png_get_y_offset_inches; - png_get_y_offset_inches_fixed; - png_get_pHYs_dpi; - png_get_io_state; - png_get_io_chunk_type; - png_get_uint_32; - png_get_uint_16; - png_get_int_32; - png_get_uint_31; - png_save_uint_32; - png_save_int_32; - png_save_uint_16; - png_set_check_for_invalid_index; - png_get_palette_max; - png_image_begin_read_from_file; - png_image_begin_read_from_stdio; - png_image_begin_read_from_memory; - png_image_finish_read; - png_image_free; - png_image_write_to_file; - png_image_write_to_stdio; - png_image_write_to_memory; - png_set_option; - png_get_acTL; - png_set_acTL; - png_get_num_frames; - png_get_num_plays; - png_get_next_frame_fcTL; - png_set_next_frame_fcTL; - png_get_next_frame_width; - png_get_next_frame_height; - png_get_next_frame_x_offset; - png_get_next_frame_y_offset; - png_get_next_frame_delay_num; - png_get_next_frame_delay_den; - png_get_next_frame_dispose_op; - png_get_next_frame_blend_op; - png_get_first_frame_is_hidden; - png_set_first_frame_is_hidden; - png_read_frame_head; - png_set_progressive_frame_fn; - png_write_frame_head; - png_write_frame_tail; -local: *; }; diff --git a/contrib/libs/libpng/patches/apng.patch b/contrib/libs/libpng/patches/apng.patch new file mode 100644 index 0000000000..0f7fcdc07b --- /dev/null +++ b/contrib/libs/libpng/patches/apng.patch @@ -0,0 +1,1577 @@ +Index: LICENSE +=================================================================== +--- a/LICENSE ++++ b/LICENSE +@@ -1,6 +1,12 @@ + COPYRIGHT NOTICE, DISCLAIMER, and LICENSE + ========================================= + ++This modified version of libpng code adds animated PNG support and is ++released under the libpng license described below. The modifications are ++Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2024 Max Stepin, ++and are delimited by "#ifdef PNG_APNG_SUPPORTED / #endif" directives ++surrounding them in the modified libpng source files. ++ + PNG Reference Library License version 2 + --------------------------------------- + +Index: pngread.c +=================================================================== +--- a/pngread.c ++++ b/pngread.c +@@ -161,6 +161,9 @@ + + else if (chunk_name == png_IDAT) + { ++#ifdef PNG_READ_APNG_SUPPORTED ++ png_have_info(png_ptr, info_ptr); ++#endif + png_ptr->idat_size = length; + break; + } +@@ -255,6 +258,17 @@ + png_handle_iTXt(png_ptr, info_ptr, length); + #endif + ++#ifdef PNG_READ_APNG_SUPPORTED ++ else if (chunk_name == png_acTL) ++ png_handle_acTL(png_ptr, info_ptr, length); ++ ++ else if (chunk_name == png_fcTL) ++ png_handle_fcTL(png_ptr, info_ptr, length); ++ ++ else if (chunk_name == png_fdAT) ++ png_handle_fdAT(png_ptr, info_ptr, length); ++#endif ++ + else + png_handle_unknown(png_ptr, info_ptr, length, + PNG_HANDLE_CHUNK_AS_DEFAULT); +@@ -262,6 +276,72 @@ + } + #endif /* SEQUENTIAL_READ */ + ++#ifdef PNG_READ_APNG_SUPPORTED ++void PNGAPI ++png_read_frame_head(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_byte have_chunk_after_DAT; /* after IDAT or after fdAT */ ++ ++ png_debug(0, "Reading frame head"); ++ ++ if ((png_ptr->mode & PNG_HAVE_acTL) == 0) ++ png_error(png_ptr, "attempt to png_read_frame_head() but " ++ "no acTL present"); ++ ++ /* do nothing for the main IDAT */ ++ if (png_ptr->num_frames_read == 0) ++ return; ++ ++ png_read_reset(png_ptr); ++ png_ptr->flags &= ~PNG_FLAG_ROW_INIT; ++ png_ptr->mode &= ~PNG_HAVE_fcTL; ++ ++ have_chunk_after_DAT = 0; ++ for (;;) ++ { ++ png_uint_32 length = png_read_chunk_header(png_ptr); ++ ++ if (png_ptr->chunk_name == png_IDAT) ++ { ++ /* discard trailing IDATs for the first frame */ ++ if (have_chunk_after_DAT != 0 || png_ptr->num_frames_read > 1) ++ png_error(png_ptr, "png_read_frame_head(): out of place IDAT"); ++ png_crc_finish(png_ptr, length); ++ } ++ ++ else if (png_ptr->chunk_name == png_fcTL) ++ { ++ png_handle_fcTL(png_ptr, info_ptr, length); ++ have_chunk_after_DAT = 1; ++ } ++ ++ else if (png_ptr->chunk_name == png_fdAT) ++ { ++ png_ensure_sequence_number(png_ptr, length); ++ ++ /* discard trailing fdATs for frames other than the first */ ++ if (have_chunk_after_DAT == 0 && png_ptr->num_frames_read > 1) ++ png_crc_finish(png_ptr, length - 4); ++ else if (png_ptr->mode & PNG_HAVE_fcTL) ++ { ++ png_ptr->idat_size = length - 4; ++ png_ptr->mode |= PNG_HAVE_IDAT; ++ ++ break; ++ } ++ else ++ png_error(png_ptr, "png_read_frame_head(): out of place fdAT"); ++ } ++ else ++ { ++ png_warning(png_ptr, "Skipped (ignored) a chunk " ++ "between APNG chunks"); ++ png_crc_finish(png_ptr, length); ++ } ++ } ++} ++#endif /* READ_APNG */ ++ + /* Optional call to update the users info_ptr structure */ + void PNGAPI + png_read_update_info(png_structrp png_ptr, png_inforp info_ptr) +Index: pngget.c +=================================================================== +--- a/pngget.c ++++ b/pngget.c +@@ -1264,4 +1264,166 @@ + # endif + #endif + ++#ifdef PNG_APNG_SUPPORTED ++png_uint_32 PNGAPI ++png_get_acTL(png_structp png_ptr, png_infop info_ptr, ++ png_uint_32 *num_frames, png_uint_32 *num_plays) ++{ ++ png_debug1(1, "in %s retrieval function", "acTL"); ++ ++ if (png_ptr != NULL && info_ptr != NULL && ++ (info_ptr->valid & PNG_INFO_acTL) != 0 && ++ num_frames != NULL && num_plays != NULL) ++ { ++ *num_frames = info_ptr->num_frames; ++ *num_plays = info_ptr->num_plays; ++ return (1); ++ } ++ ++ return (0); ++} ++ ++png_uint_32 PNGAPI ++png_get_num_frames(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_debug(1, "in png_get_num_frames()"); ++ ++ if (png_ptr != NULL && info_ptr != NULL) ++ return (info_ptr->num_frames); ++ return (0); ++} ++ ++png_uint_32 PNGAPI ++png_get_num_plays(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_debug(1, "in png_get_num_plays()"); ++ ++ if (png_ptr != NULL && info_ptr != NULL) ++ return (info_ptr->num_plays); ++ return (0); ++} ++ ++png_uint_32 PNGAPI ++png_get_next_frame_fcTL(png_structp png_ptr, png_infop info_ptr, ++ png_uint_32 *width, png_uint_32 *height, ++ png_uint_32 *x_offset, png_uint_32 *y_offset, ++ png_uint_16 *delay_num, png_uint_16 *delay_den, ++ png_byte *dispose_op, png_byte *blend_op) ++{ ++ png_debug1(1, "in %s retrieval function", "fcTL"); ++ ++ if (png_ptr != NULL && info_ptr != NULL && ++ (info_ptr->valid & PNG_INFO_fcTL) != 0 && ++ width != NULL && height != NULL && ++ x_offset != NULL && y_offset != NULL && ++ delay_num != NULL && delay_den != NULL && ++ dispose_op != NULL && blend_op != NULL) ++ { ++ *width = info_ptr->next_frame_width; ++ *height = info_ptr->next_frame_height; ++ *x_offset = info_ptr->next_frame_x_offset; ++ *y_offset = info_ptr->next_frame_y_offset; ++ *delay_num = info_ptr->next_frame_delay_num; ++ *delay_den = info_ptr->next_frame_delay_den; ++ *dispose_op = info_ptr->next_frame_dispose_op; ++ *blend_op = info_ptr->next_frame_blend_op; ++ return (1); ++ } ++ ++ return (0); ++} ++ ++png_uint_32 PNGAPI ++png_get_next_frame_width(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_debug(1, "in png_get_next_frame_width()"); ++ ++ if (png_ptr != NULL && info_ptr != NULL) ++ return (info_ptr->next_frame_width); ++ return (0); ++} ++ ++png_uint_32 PNGAPI ++png_get_next_frame_height(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_debug(1, "in png_get_next_frame_height()"); ++ ++ if (png_ptr != NULL && info_ptr != NULL) ++ return (info_ptr->next_frame_height); ++ return (0); ++} ++ ++png_uint_32 PNGAPI ++png_get_next_frame_x_offset(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_debug(1, "in png_get_next_frame_x_offset()"); ++ ++ if (png_ptr != NULL && info_ptr != NULL) ++ return (info_ptr->next_frame_x_offset); ++ return (0); ++} ++ ++png_uint_32 PNGAPI ++png_get_next_frame_y_offset(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_debug(1, "in png_get_next_frame_y_offset()"); ++ ++ if (png_ptr != NULL && info_ptr != NULL) ++ return (info_ptr->next_frame_y_offset); ++ return (0); ++} ++ ++png_uint_16 PNGAPI ++png_get_next_frame_delay_num(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_debug(1, "in png_get_next_frame_delay_num()"); ++ ++ if (png_ptr != NULL && info_ptr != NULL) ++ return (info_ptr->next_frame_delay_num); ++ return (0); ++} ++ ++png_uint_16 PNGAPI ++png_get_next_frame_delay_den(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_debug(1, "in png_get_next_frame_delay_den()"); ++ ++ if (png_ptr != NULL && info_ptr != NULL) ++ return (info_ptr->next_frame_delay_den); ++ return (0); ++} ++ ++png_byte PNGAPI ++png_get_next_frame_dispose_op(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_debug(1, "in png_get_next_frame_dispose_op()"); ++ ++ if (png_ptr != NULL && info_ptr != NULL) ++ return (info_ptr->next_frame_dispose_op); ++ return (0); ++} ++ ++png_byte PNGAPI ++png_get_next_frame_blend_op(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_debug(1, "in png_get_next_frame_blend_op()"); ++ ++ if (png_ptr != NULL && info_ptr != NULL) ++ return (info_ptr->next_frame_blend_op); ++ return (0); ++} ++ ++png_byte PNGAPI ++png_get_first_frame_is_hidden(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_debug(1, "in png_first_frame_is_hidden()"); ++ ++ if (png_ptr != NULL) ++ return (png_byte)(png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN); ++ ++ PNG_UNUSED(info_ptr) ++ ++ return 0; ++} ++#endif /* APNG */ + #endif /* READ || WRITE */ +Index: png.h +=================================================================== +--- a/png.h ++++ b/png.h +@@ -24,6 +24,12 @@ + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE + * ========================================= + * ++ * This modified version of libpng code adds animated PNG support and is ++ * released under the libpng license described below. The modifications are ++ * Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2024 Max Stepin, ++ * and are delimited by "#ifdef PNG_APNG_SUPPORTED / #endif" directives ++ * surrounding them in the modified libpng source files. ++ * + * PNG Reference Library License version 2 + * --------------------------------------- + * +@@ -329,6 +336,10 @@ + # include "pnglibconf.h" + #endif + ++#define PNG_APNG_SUPPORTED ++#define PNG_READ_APNG_SUPPORTED ++#define PNG_WRITE_APNG_SUPPORTED ++ + #ifndef PNG_VERSION_INFO_ONLY + /* Machine specific configuration. */ + # include "pngconf.h" +@@ -424,6 +435,17 @@ + * See pngconf.h for base types that vary by machine/system + */ + ++#ifdef PNG_APNG_SUPPORTED ++/* dispose_op flags from inside fcTL */ ++#define PNG_DISPOSE_OP_NONE 0x00 ++#define PNG_DISPOSE_OP_BACKGROUND 0x01 ++#define PNG_DISPOSE_OP_PREVIOUS 0x02 ++ ++/* blend_op flags from inside fcTL */ ++#define PNG_BLEND_OP_SOURCE 0x00 ++#define PNG_BLEND_OP_OVER 0x01 ++#endif /* APNG */ ++ + /* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +@@ -745,6 +767,10 @@ + #define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ + #define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ + #define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ ++#ifdef PNG_APNG_SUPPORTED ++#define PNG_INFO_acTL 0x20000U ++#define PNG_INFO_fcTL 0x40000U ++#endif + + /* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using +@@ -782,6 +808,10 @@ + #ifdef PNG_PROGRESSIVE_READ_SUPPORTED + typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); + typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); ++#ifdef PNG_APNG_SUPPORTED ++typedef PNG_CALLBACK(void, *png_progressive_frame_ptr, (png_structp, ++ png_uint_32)); ++#endif + + /* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the +@@ -3230,6 +3260,75 @@ + * END OF HARDWARE AND SOFTWARE OPTIONS + ******************************************************************************/ + ++#ifdef PNG_APNG_SUPPORTED ++PNG_EXPORT(250, png_uint_32, png_get_acTL, (png_structp png_ptr, ++ png_infop info_ptr, png_uint_32 *num_frames, png_uint_32 *num_plays)); ++ ++PNG_EXPORT(251, png_uint_32, png_set_acTL, (png_structp png_ptr, ++ png_infop info_ptr, png_uint_32 num_frames, png_uint_32 num_plays)); ++ ++PNG_EXPORT(252, png_uint_32, png_get_num_frames, (png_structp png_ptr, ++ png_infop info_ptr)); ++ ++PNG_EXPORT(253, png_uint_32, png_get_num_plays, (png_structp png_ptr, ++ png_infop info_ptr)); ++ ++PNG_EXPORT(254, png_uint_32, png_get_next_frame_fcTL, ++ (png_structp png_ptr, png_infop info_ptr, png_uint_32 *width, ++ png_uint_32 *height, png_uint_32 *x_offset, png_uint_32 *y_offset, ++ png_uint_16 *delay_num, png_uint_16 *delay_den, png_byte *dispose_op, ++ png_byte *blend_op)); ++ ++PNG_EXPORT(255, png_uint_32, png_set_next_frame_fcTL, ++ (png_structp png_ptr, png_infop info_ptr, png_uint_32 width, ++ png_uint_32 height, png_uint_32 x_offset, png_uint_32 y_offset, ++ png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, ++ png_byte blend_op)); ++ ++PNG_EXPORT(256, png_uint_32, png_get_next_frame_width, ++ (png_structp png_ptr, png_infop info_ptr)); ++PNG_EXPORT(257, png_uint_32, png_get_next_frame_height, ++ (png_structp png_ptr, png_infop info_ptr)); ++PNG_EXPORT(258, png_uint_32, png_get_next_frame_x_offset, ++ (png_structp png_ptr, png_infop info_ptr)); ++PNG_EXPORT(259, png_uint_32, png_get_next_frame_y_offset, ++ (png_structp png_ptr, png_infop info_ptr)); ++PNG_EXPORT(260, png_uint_16, png_get_next_frame_delay_num, ++ (png_structp png_ptr, png_infop info_ptr)); ++PNG_EXPORT(261, png_uint_16, png_get_next_frame_delay_den, ++ (png_structp png_ptr, png_infop info_ptr)); ++PNG_EXPORT(262, png_byte, png_get_next_frame_dispose_op, ++ (png_structp png_ptr, png_infop info_ptr)); ++PNG_EXPORT(263, png_byte, png_get_next_frame_blend_op, ++ (png_structp png_ptr, png_infop info_ptr)); ++PNG_EXPORT(264, png_byte, png_get_first_frame_is_hidden, ++ (png_structp png_ptr, png_infop info_ptr)); ++PNG_EXPORT(265, png_uint_32, png_set_first_frame_is_hidden, ++ (png_structp png_ptr, png_infop info_ptr, png_byte is_hidden)); ++ ++#ifdef PNG_READ_APNG_SUPPORTED ++PNG_EXPORT(266, void, png_read_frame_head, (png_structp png_ptr, ++ png_infop info_ptr)); ++#ifdef PNG_PROGRESSIVE_READ_SUPPORTED ++PNG_EXPORT(267, void, png_set_progressive_frame_fn, (png_structp png_ptr, ++ png_progressive_frame_ptr frame_info_fn, ++ png_progressive_frame_ptr frame_end_fn)); ++#endif /* PROGRESSIVE_READ */ ++#endif /* READ_APNG */ ++ ++#ifdef PNG_WRITE_APNG_SUPPORTED ++PNG_EXPORT(268, void, png_write_frame_head, (png_structp png_ptr, ++ png_infop info_ptr, png_bytepp row_pointers, ++ png_uint_32 width, png_uint_32 height, ++ png_uint_32 x_offset, png_uint_32 y_offset, ++ png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, ++ png_byte blend_op)); ++ ++PNG_EXPORT(269, void, png_write_frame_tail, (png_structp png_ptr, ++ png_infop info_ptr)); ++#endif /* WRITE_APNG */ ++#endif /* APNG */ ++ + /* Maintainer: Put new public prototypes here ^, in libpng.3, in project + * defs, and in scripts/symbols.def. + */ +@@ -3238,7 +3337,11 @@ + * one to use is one more than this.) + */ + #ifdef PNG_EXPORT_LAST_ORDINAL ++#ifdef PNG_APNG_SUPPORTED ++ PNG_EXPORT_LAST_ORDINAL(269); ++#else + PNG_EXPORT_LAST_ORDINAL(249); ++#endif /* APNG */ + #endif + + #ifdef __cplusplus +Index: pngpriv.h +=================================================================== +--- a/pngpriv.h ++++ b/pngpriv.h +@@ -621,6 +621,10 @@ + #define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000U /* Have another chunk after IDAT */ + #define PNG_WROTE_eXIf 0x4000U + #define PNG_IS_READ_STRUCT 0x8000U /* Else is a write struct */ ++#ifdef PNG_APNG_SUPPORTED ++#define PNG_HAVE_acTL 0x10000U ++#define PNG_HAVE_fcTL 0x20000U ++#endif + + /* Flags for the transformations the PNG library does on the image data */ + #define PNG_BGR 0x0001U +@@ -857,6 +861,16 @@ + #define png_tRNS PNG_U32(116, 82, 78, 83) + #define png_zTXt PNG_U32(122, 84, 88, 116) + ++#ifdef PNG_APNG_SUPPORTED ++#define png_acTL PNG_U32( 97, 99, 84, 76) ++#define png_fcTL PNG_U32(102, 99, 84, 76) ++#define png_fdAT PNG_U32(102, 100, 65, 84) ++ ++/* For png_struct.apng_flags: */ ++#define PNG_FIRST_FRAME_HIDDEN 0x0001U ++#define PNG_APNG_APP 0x0002U ++#endif ++ + /* The following will work on (signed char*) strings, whereas the get_uint_32 + * macro will fail on top-bit-set values because of the sign extension. + */ +@@ -1642,6 +1656,49 @@ + + #endif /* PROGRESSIVE_READ */ + ++#ifdef PNG_APNG_SUPPORTED ++PNG_INTERNAL_FUNCTION(void,png_ensure_fcTL_is_valid,(png_structp png_ptr, ++ png_uint_32 width, png_uint_32 height, ++ png_uint_32 x_offset, png_uint_32 y_offset, ++ png_uint_16 delay_num, png_uint_16 delay_den, ++ png_byte dispose_op, png_byte blend_op),PNG_EMPTY); ++ ++#ifdef PNG_READ_APNG_SUPPORTED ++PNG_INTERNAL_FUNCTION(void,png_handle_acTL,(png_structp png_ptr, ++ png_infop info_ptr, png_uint_32 length),PNG_EMPTY); ++PNG_INTERNAL_FUNCTION(void,png_handle_fcTL,(png_structp png_ptr, ++ png_infop info_ptr, png_uint_32 length),PNG_EMPTY); ++PNG_INTERNAL_FUNCTION(void,png_handle_fdAT,(png_structp png_ptr, ++ png_infop info_ptr, png_uint_32 length),PNG_EMPTY); ++PNG_INTERNAL_FUNCTION(void,png_have_info,(png_structp png_ptr, ++ png_infop info_ptr),PNG_EMPTY); ++PNG_INTERNAL_FUNCTION(void,png_ensure_sequence_number,(png_structp png_ptr, ++ png_uint_32 length),PNG_EMPTY); ++PNG_INTERNAL_FUNCTION(void,png_read_reset,(png_structp png_ptr),PNG_EMPTY); ++PNG_INTERNAL_FUNCTION(void,png_read_reinit,(png_structp png_ptr, ++ png_infop info_ptr),PNG_EMPTY); ++#ifdef PNG_PROGRESSIVE_READ_SUPPORTED ++PNG_INTERNAL_FUNCTION(void,png_progressive_read_reset,(png_structp png_ptr), ++ PNG_EMPTY); ++#endif /* PROGRESSIVE_READ */ ++#endif /* READ_APNG */ ++ ++#ifdef PNG_WRITE_APNG_SUPPORTED ++PNG_INTERNAL_FUNCTION(void,png_write_acTL,(png_structp png_ptr, ++ png_uint_32 num_frames, png_uint_32 num_plays),PNG_EMPTY); ++PNG_INTERNAL_FUNCTION(void,png_write_fcTL,(png_structp png_ptr, ++ png_uint_32 width, png_uint_32 height, ++ png_uint_32 x_offset, png_uint_32 y_offset, ++ png_uint_16 delay_num, png_uint_16 delay_den, ++ png_byte dispose_op, png_byte blend_op),PNG_EMPTY); ++PNG_INTERNAL_FUNCTION(void,png_write_fdAT,(png_structp png_ptr, ++ png_const_bytep data, size_t length),PNG_EMPTY); ++PNG_INTERNAL_FUNCTION(void,png_write_reset,(png_structp png_ptr),PNG_EMPTY); ++PNG_INTERNAL_FUNCTION(void,png_write_reinit,(png_structp png_ptr, ++ png_infop info_ptr, png_uint_32 width, png_uint_32 height),PNG_EMPTY); ++#endif /* WRITE_APNG */ ++#endif /* APNG */ ++ + /* Added at libpng version 1.6.0 */ + #ifdef PNG_GAMMA_SUPPORTED + PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr, +Index: pnginfo.h +=================================================================== +--- a/pnginfo.h ++++ b/pnginfo.h +@@ -263,5 +263,18 @@ + png_bytepp row_pointers; /* the image bits */ + #endif + ++#ifdef PNG_APNG_SUPPORTED ++ png_uint_32 num_frames; /* including default image */ ++ png_uint_32 num_plays; ++ png_uint_32 next_frame_width; ++ png_uint_32 next_frame_height; ++ png_uint_32 next_frame_x_offset; ++ png_uint_32 next_frame_y_offset; ++ png_uint_16 next_frame_delay_num; ++ png_uint_16 next_frame_delay_den; ++ png_byte next_frame_dispose_op; ++ png_byte next_frame_blend_op; ++#endif ++ + }; + #endif /* PNGINFO_H */ +Index: pngstruct.h +=================================================================== +--- a/pngstruct.h ++++ b/pngstruct.h +@@ -399,6 +399,27 @@ + png_byte filter_type; + #endif + ++#ifdef PNG_APNG_SUPPORTED ++ png_uint_32 apng_flags; ++ png_uint_32 next_seq_num; /* next fcTL/fdAT chunk sequence number */ ++ png_uint_32 first_frame_width; ++ png_uint_32 first_frame_height; ++ ++#ifdef PNG_READ_APNG_SUPPORTED ++ png_uint_32 num_frames_read; /* incremented after all image data of */ ++ /* a frame is read */ ++#ifdef PNG_PROGRESSIVE_READ_SUPPORTED ++ png_progressive_frame_ptr frame_info_fn; /* frame info read callback */ ++ png_progressive_frame_ptr frame_end_fn; /* frame data read callback */ ++#endif ++#endif ++ ++#ifdef PNG_WRITE_APNG_SUPPORTED ++ png_uint_32 num_frames_to_write; ++ png_uint_32 num_frames_written; ++#endif ++#endif /* APNG */ ++ + /* New members added in libpng-1.2.0 */ + + /* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +Index: pngwrite.c +=================================================================== +--- a/pngwrite.c ++++ b/pngwrite.c +@@ -128,6 +128,10 @@ + * the application continues writing the PNG. So check the 'invalid' + * flag here too. + */ ++#ifdef PNG_WRITE_APNG_SUPPORTED ++ if ((info_ptr->valid & PNG_INFO_acTL) != 0) ++ png_write_acTL(png_ptr, info_ptr->num_frames, info_ptr->num_plays); ++#endif + #ifdef PNG_GAMMA_SUPPORTED + # ifdef PNG_WRITE_gAMA_SUPPORTED + if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 && +@@ -368,7 +372,12 @@ + if ((png_ptr->mode & PNG_HAVE_IDAT) == 0) + png_error(png_ptr, "No IDATs written into file"); + ++#ifdef PNG_WRITE_APNG_SUPPORTED ++ if (png_ptr->num_frames_written != png_ptr->num_frames_to_write) ++ png_error(png_ptr, "Not enough frames written"); ++#endif ++ + #ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED + if (png_ptr->num_palette_max > png_ptr->num_palette) + png_benign_error(png_ptr, "Wrote palette index exceeding num_palette"); + #endif +@@ -2415,4 +2424,42 @@ + } + #endif /* SIMPLIFIED_WRITE_STDIO */ + #endif /* SIMPLIFIED_WRITE */ ++ ++#ifdef PNG_WRITE_APNG_SUPPORTED ++void PNGAPI ++png_write_frame_head(png_structp png_ptr, png_infop info_ptr, ++ png_bytepp row_pointers, png_uint_32 width, png_uint_32 height, ++ png_uint_32 x_offset, png_uint_32 y_offset, ++ png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, ++ png_byte blend_op) ++{ ++ png_debug(1, "in png_write_frame_head"); ++ ++ /* there is a chance this has been set after png_write_info was called, ++ * so it would be set but not written. is there a way to be sure? */ ++ if ((info_ptr->valid & PNG_INFO_acTL) == 0) ++ png_error(png_ptr, "png_write_frame_head(): acTL not set"); ++ ++ png_write_reset(png_ptr); ++ ++ png_write_reinit(png_ptr, info_ptr, width, height); ++ ++ if ((png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN) == 0 || ++ png_ptr->num_frames_written != 0) ++ png_write_fcTL(png_ptr, width, height, x_offset, y_offset, ++ delay_num, delay_den, dispose_op, blend_op); ++ ++ PNG_UNUSED(row_pointers) ++} ++ ++void PNGAPI ++png_write_frame_tail(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_debug(1, "in png_write_frame_tail"); ++ ++ png_ptr->num_frames_written++; ++ ++ PNG_UNUSED(info_ptr) ++} ++#endif /* WRITE_APNG */ + #endif /* WRITE */ +Index: pngpread.c +=================================================================== +--- a/pngpread.c ++++ b/pngpread.c +@@ -195,6 +195,89 @@ + + chunk_name = png_ptr->chunk_name; + ++#ifdef PNG_READ_APNG_SUPPORTED ++ if (png_ptr->num_frames_read > 0 && ++ png_ptr->num_frames_read < info_ptr->num_frames) ++ { ++ if (chunk_name == png_IDAT) ++ { ++ /* Discard trailing IDATs for the first frame */ ++ if ((png_ptr->mode & PNG_HAVE_fcTL) != 0 || ++ png_ptr->num_frames_read > 1) ++ png_error(png_ptr, "out of place IDAT"); ++ ++ PNG_PUSH_SAVE_BUFFER_IF_FULL ++ png_crc_finish(png_ptr, png_ptr->push_length); ++ png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; ++ } ++ ++ else if (chunk_name == png_fdAT) ++ { ++ PNG_PUSH_SAVE_BUFFER_IF_LT(4) ++ png_ensure_sequence_number(png_ptr, 4); ++ ++ if ((png_ptr->mode & PNG_HAVE_fcTL) == 0) ++ { ++ /* Discard trailing fdATs for frames other than the first */ ++ if (png_ptr->num_frames_read < 2) ++ png_error(png_ptr, "out of place fdAT"); ++ ++ PNG_PUSH_SAVE_BUFFER_IF_FULL ++ png_crc_finish(png_ptr, png_ptr->push_length); ++ png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; ++ } ++ ++ else ++ { ++ /* frame data follows */ ++ png_ptr->idat_size = png_ptr->push_length - 4; ++ png_ptr->mode |= PNG_HAVE_IDAT; ++ png_ptr->process_mode = PNG_READ_IDAT_MODE; ++ } ++ } ++ ++ else if (chunk_name == png_fcTL) ++ { ++ PNG_PUSH_SAVE_BUFFER_IF_FULL ++ png_read_reset(png_ptr); ++ png_ptr->mode &= ~PNG_HAVE_fcTL; ++ ++ png_handle_fcTL(png_ptr, info_ptr, png_ptr->push_length); ++ ++ if ((png_ptr->mode & PNG_HAVE_fcTL) == 0) ++ png_error(png_ptr, "missing required fcTL chunk"); ++ ++ png_read_reinit(png_ptr, info_ptr); ++ png_progressive_read_reset(png_ptr); ++ ++ if (png_ptr->frame_info_fn != NULL) ++ (*(png_ptr->frame_info_fn))(png_ptr, png_ptr->num_frames_read); ++ ++ png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; ++ } ++ ++ else if (chunk_name == png_IEND) ++ { ++ PNG_PUSH_SAVE_BUFFER_IF_FULL ++ png_warning(png_ptr, "Number of actual frames fewer than expected"); ++ png_crc_finish(png_ptr, png_ptr->push_length); ++ png_ptr->process_mode = PNG_READ_DONE_MODE; ++ png_push_have_end(png_ptr, info_ptr); ++ } ++ ++ else ++ { ++ PNG_PUSH_SAVE_BUFFER_IF_FULL ++ png_warning(png_ptr, "Skipped (ignored) a chunk " ++ "between APNG chunks"); ++ png_crc_finish(png_ptr, png_ptr->push_length); ++ png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; ++ } ++ ++ return; ++ } ++#endif /* READ_APNG */ ++ + if (chunk_name == png_IDAT) + { + if ((png_ptr->mode & PNG_AFTER_IDAT) != 0) +@@ -261,6 +344,9 @@ + + else if (chunk_name == png_IDAT) + { ++#ifdef PNG_READ_APNG_SUPPORTED ++ png_have_info(png_ptr, info_ptr); ++#endif + png_ptr->idat_size = png_ptr->push_length; + png_ptr->process_mode = PNG_READ_IDAT_MODE; + png_push_have_info(png_ptr, info_ptr); +@@ -415,6 +501,20 @@ + } + #endif + ++#ifdef PNG_READ_APNG_SUPPORTED ++ else if (chunk_name == png_acTL) ++ { ++ PNG_PUSH_SAVE_BUFFER_IF_FULL ++ png_handle_acTL(png_ptr, info_ptr, png_ptr->push_length); ++ } ++ ++ else if (chunk_name == png_fcTL) ++ { ++ PNG_PUSH_SAVE_BUFFER_IF_FULL ++ png_handle_fcTL(png_ptr, info_ptr, png_ptr->push_length); ++ } ++ ++#endif /* READ_APNG */ + else + { + PNG_PUSH_SAVE_BUFFER_IF_FULL +@@ -547,7 +647,11 @@ + png_byte chunk_tag[4]; + + /* TODO: this code can be commoned up with the same code in push_read */ ++#ifdef PNG_READ_APNG_SUPPORTED ++ PNG_PUSH_SAVE_BUFFER_IF_LT(12) ++#else + PNG_PUSH_SAVE_BUFFER_IF_LT(8) ++#endif + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); +@@ -555,17 +659,60 @@ + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + ++#ifdef PNG_READ_APNG_SUPPORTED ++ if (png_ptr->chunk_name != png_fdAT && png_ptr->num_frames_read > 0) ++ { ++ if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) != 0) ++ { ++ png_ptr->process_mode = PNG_READ_CHUNK_MODE; ++ if (png_ptr->frame_end_fn != NULL) ++ (*(png_ptr->frame_end_fn))(png_ptr, png_ptr->num_frames_read); ++ png_ptr->num_frames_read++; ++ return; ++ } ++ else ++ { ++ if (png_ptr->chunk_name == png_IEND) ++ png_error(png_ptr, "Not enough image data"); ++ PNG_PUSH_SAVE_BUFFER_IF_FULL ++ png_warning(png_ptr, "Skipping (ignoring) a chunk between " ++ "APNG chunks"); ++ png_crc_finish(png_ptr, png_ptr->push_length); ++ png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; ++ return; ++ } ++ } ++ else ++#endif ++#ifdef PNG_READ_APNG_SUPPORTED ++ if (png_ptr->chunk_name != png_IDAT && png_ptr->num_frames_read == 0) ++#else + if (png_ptr->chunk_name != png_IDAT) ++#endif + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + + if ((png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED) == 0) + png_error(png_ptr, "Not enough compressed data"); + ++#ifdef PNG_READ_APNG_SUPPORTED ++ if (png_ptr->frame_end_fn != NULL) ++ (*(png_ptr->frame_end_fn))(png_ptr, png_ptr->num_frames_read); ++ png_ptr->num_frames_read++; ++#endif ++ + return; + } + + png_ptr->idat_size = png_ptr->push_length; ++ ++#ifdef PNG_READ_APNG_SUPPORTED ++ if (png_ptr->num_frames_read > 0) ++ { ++ png_ensure_sequence_number(png_ptr, 4); ++ png_ptr->idat_size -= 4; ++ } ++#endif + } + + if (png_ptr->idat_size != 0 && png_ptr->save_buffer_size != 0) +@@ -639,6 +786,16 @@ + if (!(buffer_length > 0) || buffer == NULL) + png_error(png_ptr, "No IDAT data (internal error)"); + ++#ifdef PNG_READ_APNG_SUPPORTED ++ /* If the app is not APNG-aware, decode only the first frame */ ++ if ((png_ptr->apng_flags & PNG_APNG_APP) == 0 && ++ png_ptr->num_frames_read > 0) ++ { ++ png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; ++ return; ++ } ++#endif ++ + /* This routine must process all the data it has been given + * before returning, calling the row callback as required to + * handle the uncompressed results. +@@ -1093,6 +1250,18 @@ + png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer); + } + ++#ifdef PNG_READ_APNG_SUPPORTED ++void PNGAPI ++png_set_progressive_frame_fn(png_structp png_ptr, ++ png_progressive_frame_ptr frame_info_fn, ++ png_progressive_frame_ptr frame_end_fn) ++{ ++ png_ptr->frame_info_fn = frame_info_fn; ++ png_ptr->frame_end_fn = frame_end_fn; ++ png_ptr->apng_flags |= PNG_APNG_APP; ++} ++#endif ++ + png_voidp PNGAPI + png_get_progressive_ptr(png_const_structrp png_ptr) + { +Index: pngset.c +=================================================================== +--- a/pngset.c ++++ b/pngset.c +@@ -280,6 +280,11 @@ + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); + + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); ++ ++#ifdef PNG_APNG_SUPPORTED ++ /* for non-animated png. this may be overwritten from an acTL chunk later */ ++ info_ptr->num_frames = 1; ++#endif + } + + #ifdef PNG_oFFs_SUPPORTED +@@ -1151,6 +1156,146 @@ + } + #endif /* sPLT */ + ++#ifdef PNG_APNG_SUPPORTED ++png_uint_32 PNGAPI ++png_set_acTL(png_structp png_ptr, png_infop info_ptr, ++ png_uint_32 num_frames, png_uint_32 num_plays) ++{ ++ png_debug1(1, "in %s storage function", "acTL"); ++ ++ if (png_ptr == NULL || info_ptr == NULL) ++ { ++ png_warning(png_ptr, ++ "Call to png_set_acTL() with NULL png_ptr " ++ "or info_ptr ignored"); ++ return (0); ++ } ++ if (num_frames == 0) ++ { ++ png_warning(png_ptr, ++ "Ignoring attempt to set acTL with num_frames zero"); ++ return (0); ++ } ++ if (num_frames > PNG_UINT_31_MAX) ++ { ++ png_warning(png_ptr, ++ "Ignoring attempt to set acTL with num_frames > 2^31-1"); ++ return (0); ++ } ++ if (num_plays > PNG_UINT_31_MAX) ++ { ++ png_warning(png_ptr, ++ "Ignoring attempt to set acTL with num_plays > 2^31-1"); ++ return (0); ++ } ++ ++ info_ptr->num_frames = num_frames; ++ info_ptr->num_plays = num_plays; ++ ++ info_ptr->valid |= PNG_INFO_acTL; ++ ++ return (1); ++} ++ ++/* delay_num and delay_den can hold any 16-bit values including zero */ ++png_uint_32 PNGAPI ++png_set_next_frame_fcTL(png_structp png_ptr, png_infop info_ptr, ++ png_uint_32 width, png_uint_32 height, ++ png_uint_32 x_offset, png_uint_32 y_offset, ++ png_uint_16 delay_num, png_uint_16 delay_den, ++ png_byte dispose_op, png_byte blend_op) ++{ ++ png_debug1(1, "in %s storage function", "fcTL"); ++ ++ if (png_ptr == NULL || info_ptr == NULL) ++ { ++ png_warning(png_ptr, ++ "Call to png_set_fcTL() with NULL png_ptr or info_ptr " ++ "ignored"); ++ return (0); ++ } ++ ++ png_ensure_fcTL_is_valid(png_ptr, width, height, x_offset, y_offset, ++ delay_num, delay_den, dispose_op, blend_op); ++ ++ if (blend_op == PNG_BLEND_OP_OVER) ++ { ++ if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) == 0 && ++ png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) == 0) ++ { ++ png_warning(png_ptr, "PNG_BLEND_OP_OVER is meaningless " ++ "and wasteful for opaque images, ignored"); ++ blend_op = PNG_BLEND_OP_SOURCE; ++ } ++ } ++ ++ info_ptr->next_frame_width = width; ++ info_ptr->next_frame_height = height; ++ info_ptr->next_frame_x_offset = x_offset; ++ info_ptr->next_frame_y_offset = y_offset; ++ info_ptr->next_frame_delay_num = delay_num; ++ info_ptr->next_frame_delay_den = delay_den; ++ info_ptr->next_frame_dispose_op = dispose_op; ++ info_ptr->next_frame_blend_op = blend_op; ++ ++ info_ptr->valid |= PNG_INFO_fcTL; ++ ++ return (1); ++} ++ ++void /* PRIVATE */ ++png_ensure_fcTL_is_valid(png_structp png_ptr, ++ png_uint_32 width, png_uint_32 height, ++ png_uint_32 x_offset, png_uint_32 y_offset, ++ png_uint_16 delay_num, png_uint_16 delay_den, ++ png_byte dispose_op, png_byte blend_op) ++{ ++ if (width == 0 || width > PNG_UINT_31_MAX) ++ png_error(png_ptr, "invalid width in fcTL (0 or > 2^31-1)"); ++ if (height == 0 || height > PNG_UINT_31_MAX) ++ png_error(png_ptr, "invalid height in fcTL (0 or > 2^31-1)"); ++ if (x_offset > PNG_UINT_31_MAX) ++ png_error(png_ptr, "invalid x_offset in fcTL (> 2^31-1)"); ++ if (y_offset > PNG_UINT_31_MAX) ++ png_error(png_ptr, "invalid y_offset in fcTL (> 2^31-1)"); ++ if (width + x_offset > png_ptr->first_frame_width || ++ height + y_offset > png_ptr->first_frame_height) ++ png_error(png_ptr, "dimensions of a frame are greater than " ++ "the ones in IHDR"); ++ ++ if (dispose_op != PNG_DISPOSE_OP_NONE && ++ dispose_op != PNG_DISPOSE_OP_BACKGROUND && ++ dispose_op != PNG_DISPOSE_OP_PREVIOUS) ++ png_error(png_ptr, "invalid dispose_op in fcTL"); ++ ++ if (blend_op != PNG_BLEND_OP_SOURCE && ++ blend_op != PNG_BLEND_OP_OVER) ++ png_error(png_ptr, "invalid blend_op in fcTL"); ++ ++ PNG_UNUSED(delay_num) ++ PNG_UNUSED(delay_den) ++} ++ ++png_uint_32 PNGAPI ++png_set_first_frame_is_hidden(png_structp png_ptr, png_infop info_ptr, ++ png_byte is_hidden) ++{ ++ png_debug(1, "in png_first_frame_is_hidden()"); ++ ++ if (png_ptr == NULL) ++ return 0; ++ ++ if (is_hidden != 0) ++ png_ptr->apng_flags |= PNG_FIRST_FRAME_HIDDEN; ++ else ++ png_ptr->apng_flags &= ~PNG_FIRST_FRAME_HIDDEN; ++ ++ PNG_UNUSED(info_ptr) ++ ++ return 1; ++} ++#endif /* APNG */ ++ + #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED + static png_byte + check_location(png_const_structrp png_ptr, int location) +Index: pngrutil.c +=================================================================== +--- a/pngrutil.c ++++ b/pngrutil.c +@@ -863,6 +863,11 @@ + filter_type = buf[11]; + interlace_type = buf[12]; + ++#ifdef PNG_READ_APNG_SUPPORTED ++ png_ptr->first_frame_width = width; ++ png_ptr->first_frame_height = height; ++#endif ++ + /* Set internal variables */ + png_ptr->width = width; + png_ptr->height = height; +@@ -2857,6 +2862,180 @@ + } + #endif + ++#ifdef PNG_READ_APNG_SUPPORTED ++void /* PRIVATE */ ++png_handle_acTL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) ++{ ++ png_byte data[8]; ++ png_uint_32 num_frames; ++ png_uint_32 num_plays; ++ png_uint_32 didSet; ++ ++ png_debug(1, "in png_handle_acTL"); ++ ++ if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) ++ { ++ png_error(png_ptr, "Missing IHDR before acTL"); ++ } ++ else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) ++ { ++ png_warning(png_ptr, "Invalid acTL after IDAT skipped"); ++ png_crc_finish(png_ptr, length); ++ return; ++ } ++ else if ((png_ptr->mode & PNG_HAVE_acTL) != 0) ++ { ++ png_warning(png_ptr, "Duplicate acTL skipped"); ++ png_crc_finish(png_ptr, length); ++ return; ++ } ++ else if (length != 8) ++ { ++ png_warning(png_ptr, "acTL with invalid length skipped"); ++ png_crc_finish(png_ptr, length); ++ return; ++ } ++ ++ png_crc_read(png_ptr, data, 8); ++ png_crc_finish(png_ptr, 0); ++ ++ num_frames = png_get_uint_31(png_ptr, data); ++ num_plays = png_get_uint_31(png_ptr, data + 4); ++ ++ /* the set function will do error checking on num_frames */ ++ didSet = png_set_acTL(png_ptr, info_ptr, num_frames, num_plays); ++ if (didSet != 0) ++ png_ptr->mode |= PNG_HAVE_acTL; ++} ++ ++void /* PRIVATE */ ++png_handle_fcTL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) ++{ ++ png_byte data[22]; ++ png_uint_32 width; ++ png_uint_32 height; ++ png_uint_32 x_offset; ++ png_uint_32 y_offset; ++ png_uint_16 delay_num; ++ png_uint_16 delay_den; ++ png_byte dispose_op; ++ png_byte blend_op; ++ ++ png_debug(1, "in png_handle_fcTL"); ++ ++ png_ensure_sequence_number(png_ptr, length); ++ ++ if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) ++ { ++ png_error(png_ptr, "Missing IHDR before fcTL"); ++ } ++ else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) ++ { ++ /* for any frames other then the first this message may be misleading, ++ * but correct. PNG_HAVE_IDAT is unset before the frame head is read ++ * i can't think of a better message */ ++ png_warning(png_ptr, "Invalid fcTL after IDAT skipped"); ++ png_crc_finish(png_ptr, length-4); ++ return; ++ } ++ else if ((png_ptr->mode & PNG_HAVE_fcTL) != 0) ++ { ++ png_warning(png_ptr, "Duplicate fcTL within one frame skipped"); ++ png_crc_finish(png_ptr, length-4); ++ return; ++ } ++ else if (length != 26) ++ { ++ png_warning(png_ptr, "fcTL with invalid length skipped"); ++ png_crc_finish(png_ptr, length-4); ++ return; ++ } ++ ++ png_crc_read(png_ptr, data, 22); ++ png_crc_finish(png_ptr, 0); ++ ++ width = png_get_uint_31(png_ptr, data); ++ height = png_get_uint_31(png_ptr, data + 4); ++ x_offset = png_get_uint_31(png_ptr, data + 8); ++ y_offset = png_get_uint_31(png_ptr, data + 12); ++ delay_num = png_get_uint_16(data + 16); ++ delay_den = png_get_uint_16(data + 18); ++ dispose_op = data[20]; ++ blend_op = data[21]; ++ ++ if (png_ptr->num_frames_read == 0 && (x_offset != 0 || y_offset != 0)) ++ { ++ png_warning(png_ptr, "fcTL for the first frame must have zero offset"); ++ return; ++ } ++ ++ if (info_ptr != NULL) ++ { ++ if (png_ptr->num_frames_read == 0 && ++ (width != info_ptr->width || height != info_ptr->height)) ++ { ++ png_warning(png_ptr, "size in first frame's fcTL must match " ++ "the size in IHDR"); ++ return; ++ } ++ ++ /* The set function will do more error checking */ ++ png_set_next_frame_fcTL(png_ptr, info_ptr, width, height, ++ x_offset, y_offset, delay_num, delay_den, ++ dispose_op, blend_op); ++ ++ png_read_reinit(png_ptr, info_ptr); ++ ++ png_ptr->mode |= PNG_HAVE_fcTL; ++ } ++} ++ ++void /* PRIVATE */ ++png_have_info(png_structp png_ptr, png_infop info_ptr) ++{ ++ if ((info_ptr->valid & PNG_INFO_acTL) != 0 && ++ (info_ptr->valid & PNG_INFO_fcTL) == 0) ++ { ++ png_ptr->apng_flags |= PNG_FIRST_FRAME_HIDDEN; ++ info_ptr->num_frames++; ++ } ++} ++ ++void /* PRIVATE */ ++png_handle_fdAT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) ++{ ++ png_ensure_sequence_number(png_ptr, length); ++ ++ /* This function is only called from png_read_end(), png_read_info(), ++ * and png_push_read_chunk() which means that: ++ * - the user doesn't want to read this frame ++ * - or this is an out-of-place fdAT ++ * in either case it is safe to ignore the chunk with a warning */ ++ png_warning(png_ptr, "ignoring fdAT chunk"); ++ png_crc_finish(png_ptr, length - 4); ++ PNG_UNUSED(info_ptr) ++} ++ ++void /* PRIVATE */ ++png_ensure_sequence_number(png_structp png_ptr, png_uint_32 length) ++{ ++ png_byte data[4]; ++ png_uint_32 sequence_number; ++ ++ if (length < 4) ++ png_error(png_ptr, "invalid fcTL or fdAT chunk found"); ++ ++ png_crc_read(png_ptr, data, 4); ++ sequence_number = png_get_uint_31(png_ptr, data); ++ ++ if (sequence_number != png_ptr->next_seq_num) ++ png_error(png_ptr, "fcTL or fdAT chunk with out-of-order sequence " ++ "number found"); ++ ++ png_ptr->next_seq_num++; ++} ++#endif /* READ_APNG */ ++ + #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + /* Utility function for png_handle_unknown; set up png_ptr::unknown_chunk */ + static int +@@ -3162,7 +3341,11 @@ + if (PNG_USER_CHUNK_MALLOC_MAX < limit) + limit = PNG_USER_CHUNK_MALLOC_MAX; + # endif ++#ifdef PNG_READ_APNG_SUPPORTED ++ if (png_ptr->chunk_name == png_IDAT || png_ptr->chunk_name == png_fdAT) ++#else + if (png_ptr->chunk_name == png_IDAT) ++#endif + { + png_alloc_size_t idat_limit = PNG_UINT_31_MAX; + size_t row_factor = +@@ -4166,6 +4349,38 @@ + uInt avail_in; + png_bytep buffer; + ++#ifdef PNG_READ_APNG_SUPPORTED ++ png_uint_32 bytes_to_skip = 0; ++ ++ while (png_ptr->idat_size == 0 || bytes_to_skip != 0) ++ { ++ png_crc_finish(png_ptr, bytes_to_skip); ++ bytes_to_skip = 0; ++ ++ png_ptr->idat_size = png_read_chunk_header(png_ptr); ++ if (png_ptr->num_frames_read == 0) ++ { ++ if (png_ptr->chunk_name != png_IDAT) ++ png_error(png_ptr, "Not enough image data"); ++ } ++ else ++ { ++ if (png_ptr->chunk_name == png_IEND) ++ png_error(png_ptr, "Not enough image data"); ++ if (png_ptr->chunk_name != png_fdAT) ++ { ++ png_warning(png_ptr, "Skipped (ignored) a chunk " ++ "between APNG chunks"); ++ bytes_to_skip = png_ptr->idat_size; ++ continue; ++ } ++ ++ png_ensure_sequence_number(png_ptr, png_ptr->idat_size); ++ ++ png_ptr->idat_size -= 4; ++ } ++ } ++#else + while (png_ptr->idat_size == 0) + { + png_crc_finish(png_ptr, 0); +@@ -4177,6 +4392,7 @@ + if (png_ptr->chunk_name != png_IDAT) + png_error(png_ptr, "Not enough image data"); + } ++#endif /* READ_APNG */ + + avail_in = png_ptr->IDAT_read_size; + +@@ -4240,6 +4456,9 @@ + + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; ++#ifdef PNG_READ_APNG_SUPPORTED ++ png_ptr->num_frames_read++; ++#endif + + if (png_ptr->zstream.avail_in > 0 || png_ptr->idat_size > 0) + png_chunk_benign_error(png_ptr, "Extra compressed data"); +@@ -4677,4 +4896,80 @@ + + png_ptr->flags |= PNG_FLAG_ROW_INIT; + } ++ ++#ifdef PNG_READ_APNG_SUPPORTED ++/* This function is to be called after the main IDAT set has been read and ++ * before a new IDAT is read. It resets some parts of png_ptr ++ * to make them usable by the read functions again */ ++void /* PRIVATE */ ++png_read_reset(png_structp png_ptr) ++{ ++ png_ptr->mode &= ~PNG_HAVE_IDAT; ++ png_ptr->mode &= ~PNG_AFTER_IDAT; ++ png_ptr->row_number = 0; ++ png_ptr->pass = 0; ++} ++ ++void /* PRIVATE */ ++png_read_reinit(png_structp png_ptr, png_infop info_ptr) ++{ ++ png_ptr->width = info_ptr->next_frame_width; ++ png_ptr->height = info_ptr->next_frame_height; ++ png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->width); ++ png_ptr->info_rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, ++ png_ptr->width); ++ if (png_ptr->prev_row != NULL) ++ memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); ++} ++ ++#ifdef PNG_PROGRESSIVE_READ_SUPPORTED ++/* same as png_read_reset() but for the progressive reader */ ++void /* PRIVATE */ ++png_progressive_read_reset(png_structp png_ptr) ++{ ++#ifdef PNG_READ_INTERLACING_SUPPORTED ++ /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ ++ ++ /* Start of interlace block */ ++ static const png_byte png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; ++ ++ /* Offset to next interlace block */ ++ static const png_byte png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; ++ ++ /* Start of interlace block in the y direction */ ++ static const png_byte png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; ++ ++ /* Offset to next interlace block in the y direction */ ++ static const png_byte png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; ++ ++ if (png_ptr->interlaced != 0) ++ { ++ if ((png_ptr->transformations & PNG_INTERLACE) == 0) ++ png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - ++ png_pass_ystart[0]) / png_pass_yinc[0]; ++ else ++ png_ptr->num_rows = png_ptr->height; ++ ++ png_ptr->iwidth = (png_ptr->width + ++ png_pass_inc[png_ptr->pass] - 1 - ++ png_pass_start[png_ptr->pass]) / ++ png_pass_inc[png_ptr->pass]; ++ } ++ else ++#endif /* READ_INTERLACING */ ++ { ++ png_ptr->num_rows = png_ptr->height; ++ png_ptr->iwidth = png_ptr->width; ++ } ++ png_ptr->flags &= ~PNG_FLAG_ZSTREAM_ENDED; ++ if (inflateReset(&(png_ptr->zstream)) != Z_OK) ++ png_error(png_ptr, "inflateReset failed"); ++ png_ptr->zstream.avail_in = 0; ++ png_ptr->zstream.next_in = 0; ++ png_ptr->zstream.next_out = png_ptr->row_buf; ++ png_ptr->zstream.avail_out = (uInt)PNG_ROWBYTES(png_ptr->pixel_depth, ++ png_ptr->iwidth) + 1; ++} ++#endif /* PROGRESSIVE_READ */ ++#endif /* READ_APNG */ + #endif /* READ */ +Index: pngwutil.c +=================================================================== +--- a/pngwutil.c ++++ b/pngwutil.c +@@ -821,6 +821,11 @@ + /* Write the chunk */ + png_write_complete_chunk(png_ptr, png_IHDR, buf, 13); + ++#ifdef PNG_WRITE_APNG_SUPPORTED ++ png_ptr->first_frame_width = width; ++ png_ptr->first_frame_height = height; ++#endif ++ + if ((png_ptr->do_filter) == PNG_NO_FILTERS) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || +@@ -1003,7 +1008,17 @@ + #endif + + if (size > 0) ++#ifdef PNG_WRITE_APNG_SUPPORTED ++ { ++ if (png_ptr->num_frames_written == 0) ++#endif + png_write_complete_chunk(png_ptr, png_IDAT, data, size); ++#ifdef PNG_WRITE_APNG_SUPPORTED ++ else ++ png_write_fdAT(png_ptr, data, size); ++ } ++#endif /* WRITE_APNG */ ++ + png_ptr->mode |= PNG_HAVE_IDAT; + + png_ptr->zstream.next_out = data; +@@ -1050,7 +1065,17 @@ + #endif + + if (size > 0) ++#ifdef PNG_WRITE_APNG_SUPPORTED ++ { ++ if (png_ptr->num_frames_written == 0) ++#endif + png_write_complete_chunk(png_ptr, png_IDAT, data, size); ++#ifdef PNG_WRITE_APNG_SUPPORTED ++ else ++ png_write_fdAT(png_ptr, data, size); ++ } ++#endif /* WRITE_APNG */ ++ + png_ptr->zstream.avail_out = 0; + png_ptr->zstream.next_out = NULL; + png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT; +@@ -1885,6 +1910,82 @@ + } + #endif + ++#ifdef PNG_WRITE_APNG_SUPPORTED ++void /* PRIVATE */ ++png_write_acTL(png_structp png_ptr, ++ png_uint_32 num_frames, png_uint_32 num_plays) ++{ ++ png_byte buf[8]; ++ ++ png_debug(1, "in png_write_acTL"); ++ ++ png_ptr->num_frames_to_write = num_frames; ++ ++ if ((png_ptr->apng_flags & PNG_FIRST_FRAME_HIDDEN) != 0) ++ num_frames--; ++ ++ png_save_uint_32(buf, num_frames); ++ png_save_uint_32(buf + 4, num_plays); ++ ++ png_write_complete_chunk(png_ptr, png_acTL, buf, 8); ++} ++ ++void /* PRIVATE */ ++png_write_fcTL(png_structp png_ptr, png_uint_32 width, png_uint_32 height, ++ png_uint_32 x_offset, png_uint_32 y_offset, ++ png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, ++ png_byte blend_op) ++{ ++ png_byte buf[26]; ++ ++ png_debug(1, "in png_write_fcTL"); ++ ++ if (png_ptr->num_frames_written == 0 && (x_offset != 0 || y_offset != 0)) ++ png_error(png_ptr, "x and/or y offset for the first frame aren't 0"); ++ if (png_ptr->num_frames_written == 0 && ++ (width != png_ptr->first_frame_width || ++ height != png_ptr->first_frame_height)) ++ png_error(png_ptr, "width and/or height in the first frame's fcTL " ++ "don't match the ones in IHDR"); ++ ++ /* more error checking */ ++ png_ensure_fcTL_is_valid(png_ptr, width, height, x_offset, y_offset, ++ delay_num, delay_den, dispose_op, blend_op); ++ ++ png_save_uint_32(buf, png_ptr->next_seq_num); ++ png_save_uint_32(buf + 4, width); ++ png_save_uint_32(buf + 8, height); ++ png_save_uint_32(buf + 12, x_offset); ++ png_save_uint_32(buf + 16, y_offset); ++ png_save_uint_16(buf + 20, delay_num); ++ png_save_uint_16(buf + 22, delay_den); ++ buf[24] = dispose_op; ++ buf[25] = blend_op; ++ ++ png_write_complete_chunk(png_ptr, png_fcTL, buf, 26); ++ ++ png_ptr->next_seq_num++; ++} ++ ++void /* PRIVATE */ ++png_write_fdAT(png_structp png_ptr, ++ png_const_bytep data, size_t length) ++{ ++ png_byte buf[4]; ++ ++ png_write_chunk_header(png_ptr, png_fdAT, (png_uint_32)(4 + length)); ++ ++ png_save_uint_32(buf, png_ptr->next_seq_num); ++ png_write_chunk_data(png_ptr, buf, 4); ++ ++ png_write_chunk_data(png_ptr, data, length); ++ ++ png_write_chunk_end(png_ptr); ++ ++ png_ptr->next_seq_num++; ++} ++#endif /* WRITE_APNG */ ++ + /* Initializes the row writing capability of libpng */ + void /* PRIVATE */ + png_write_start_row(png_structrp png_ptr) +@@ -2778,4 +2879,39 @@ + } + #endif /* WRITE_FLUSH */ + } ++ ++#ifdef PNG_WRITE_APNG_SUPPORTED ++void /* PRIVATE */ ++png_write_reset(png_structp png_ptr) ++{ ++ png_ptr->row_number = 0; ++ png_ptr->pass = 0; ++ png_ptr->mode &= ~PNG_HAVE_IDAT; ++} ++ ++void /* PRIVATE */ ++png_write_reinit(png_structp png_ptr, png_infop info_ptr, ++ png_uint_32 width, png_uint_32 height) ++{ ++ if (png_ptr->num_frames_written == 0 && ++ (width != png_ptr->first_frame_width || ++ height != png_ptr->first_frame_height)) ++ png_error(png_ptr, "width and/or height in the first frame's fcTL " ++ "don't match the ones in IHDR"); ++ if (width > png_ptr->first_frame_width || ++ height > png_ptr->first_frame_height) ++ png_error(png_ptr, "width and/or height for a frame greater than " ++ "the ones in IHDR"); ++ ++ png_set_IHDR(png_ptr, info_ptr, width, height, ++ info_ptr->bit_depth, info_ptr->color_type, ++ info_ptr->interlace_type, info_ptr->compression_type, ++ info_ptr->filter_type); ++ ++ png_ptr->width = width; ++ png_ptr->height = height; ++ png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); ++ png_ptr->usr_width = png_ptr->width; ++} ++#endif /* WRITE_APNG */ + #endif /* WRITE */ + diff --git a/contrib/libs/libpng/png.c b/contrib/libs/libpng/png.c index 56800d8aec..d6471b06cc 100644 --- a/contrib/libs/libpng/png.c +++ b/contrib/libs/libpng/png.c @@ -815,15 +815,13 @@ png_get_copyright(png_const_structrp png_ptr) return PNG_STRING_COPYRIGHT #else return PNG_STRING_NEWLINE \ - "libpng version 1.6.40+apng" PNG_STRING_NEWLINE \ + "libpng version 1.6.40" PNG_STRING_NEWLINE \ "Copyright (c) 2018-2023 Cosmin Truta" PNG_STRING_NEWLINE \ "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \ PNG_STRING_NEWLINE \ "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ - PNG_STRING_NEWLINE \ - "Portions Copyright (c) 2006-2007 Andrew Smith" PNG_STRING_NEWLINE \ - "Portions Copyright (c) 2008-2023 Max Stepin" PNG_STRING_NEWLINE ; + PNG_STRING_NEWLINE; #endif } diff --git a/contrib/libs/libpng/png.h b/contrib/libs/libpng/png.h index 0b26e307ec..7a9848eb12 100644 --- a/contrib/libs/libpng/png.h +++ b/contrib/libs/libpng/png.h @@ -26,7 +26,7 @@ * * This modified version of libpng code adds animated PNG support and is * released under the libpng license described below. The modifications are - * Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2023 Max Stepin, + * Copyright (c) 2006-2007 Andrew Smith, Copyright (c) 2008-2024 Max Stepin, * and are delimited by "#ifdef PNG_APNG_SUPPORTED / #endif" directives * surrounding them in the modified libpng source files. * @@ -284,9 +284,8 @@ */ /* Version information for png.h - this should match the version in png.c */ -#define PNG_LIBPNG_VER_STRING "1.6.40+apng" -#define PNG_HEADER_VERSION_STRING \ - " libpng version 1.6.40+apng - June 21, 2023\n" +#define PNG_LIBPNG_VER_STRING "1.6.40" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.40 - June 21, 2023\n" #define PNG_LIBPNG_VER_SONUM 16 #define PNG_LIBPNG_VER_DLLNUM 16 @@ -3258,70 +3257,70 @@ PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, ******************************************************************************/ #ifdef PNG_APNG_SUPPORTED -PNG_EXPORT(248, png_uint_32, png_get_acTL, (png_structp png_ptr, +PNG_EXPORT(250, png_uint_32, png_get_acTL, (png_structp png_ptr, png_infop info_ptr, png_uint_32 *num_frames, png_uint_32 *num_plays)); -PNG_EXPORT(249, png_uint_32, png_set_acTL, (png_structp png_ptr, +PNG_EXPORT(251, png_uint_32, png_set_acTL, (png_structp png_ptr, png_infop info_ptr, png_uint_32 num_frames, png_uint_32 num_plays)); -PNG_EXPORT(250, png_uint_32, png_get_num_frames, (png_structp png_ptr, +PNG_EXPORT(252, png_uint_32, png_get_num_frames, (png_structp png_ptr, png_infop info_ptr)); -PNG_EXPORT(251, png_uint_32, png_get_num_plays, (png_structp png_ptr, +PNG_EXPORT(253, png_uint_32, png_get_num_plays, (png_structp png_ptr, png_infop info_ptr)); -PNG_EXPORT(252, png_uint_32, png_get_next_frame_fcTL, +PNG_EXPORT(254, png_uint_32, png_get_next_frame_fcTL, (png_structp png_ptr, png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, png_uint_32 *x_offset, png_uint_32 *y_offset, png_uint_16 *delay_num, png_uint_16 *delay_den, png_byte *dispose_op, png_byte *blend_op)); -PNG_EXPORT(253, png_uint_32, png_set_next_frame_fcTL, +PNG_EXPORT(255, png_uint_32, png_set_next_frame_fcTL, (png_structp png_ptr, png_infop info_ptr, png_uint_32 width, png_uint_32 height, png_uint_32 x_offset, png_uint_32 y_offset, png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, png_byte blend_op)); -PNG_EXPORT(254, png_uint_32, png_get_next_frame_width, +PNG_EXPORT(256, png_uint_32, png_get_next_frame_width, (png_structp png_ptr, png_infop info_ptr)); -PNG_EXPORT(255, png_uint_32, png_get_next_frame_height, +PNG_EXPORT(257, png_uint_32, png_get_next_frame_height, (png_structp png_ptr, png_infop info_ptr)); -PNG_EXPORT(256, png_uint_32, png_get_next_frame_x_offset, +PNG_EXPORT(258, png_uint_32, png_get_next_frame_x_offset, (png_structp png_ptr, png_infop info_ptr)); -PNG_EXPORT(257, png_uint_32, png_get_next_frame_y_offset, +PNG_EXPORT(259, png_uint_32, png_get_next_frame_y_offset, (png_structp png_ptr, png_infop info_ptr)); -PNG_EXPORT(258, png_uint_16, png_get_next_frame_delay_num, +PNG_EXPORT(260, png_uint_16, png_get_next_frame_delay_num, (png_structp png_ptr, png_infop info_ptr)); -PNG_EXPORT(259, png_uint_16, png_get_next_frame_delay_den, +PNG_EXPORT(261, png_uint_16, png_get_next_frame_delay_den, (png_structp png_ptr, png_infop info_ptr)); -PNG_EXPORT(260, png_byte, png_get_next_frame_dispose_op, +PNG_EXPORT(262, png_byte, png_get_next_frame_dispose_op, (png_structp png_ptr, png_infop info_ptr)); -PNG_EXPORT(261, png_byte, png_get_next_frame_blend_op, +PNG_EXPORT(263, png_byte, png_get_next_frame_blend_op, (png_structp png_ptr, png_infop info_ptr)); -PNG_EXPORT(262, png_byte, png_get_first_frame_is_hidden, +PNG_EXPORT(264, png_byte, png_get_first_frame_is_hidden, (png_structp png_ptr, png_infop info_ptr)); -PNG_EXPORT(263, png_uint_32, png_set_first_frame_is_hidden, +PNG_EXPORT(265, png_uint_32, png_set_first_frame_is_hidden, (png_structp png_ptr, png_infop info_ptr, png_byte is_hidden)); #ifdef PNG_READ_APNG_SUPPORTED -PNG_EXPORT(264, void, png_read_frame_head, (png_structp png_ptr, +PNG_EXPORT(266, void, png_read_frame_head, (png_structp png_ptr, png_infop info_ptr)); #ifdef PNG_PROGRESSIVE_READ_SUPPORTED -PNG_EXPORT(265, void, png_set_progressive_frame_fn, (png_structp png_ptr, +PNG_EXPORT(267, void, png_set_progressive_frame_fn, (png_structp png_ptr, png_progressive_frame_ptr frame_info_fn, png_progressive_frame_ptr frame_end_fn)); #endif /* PROGRESSIVE_READ */ #endif /* READ_APNG */ #ifdef PNG_WRITE_APNG_SUPPORTED -PNG_EXPORT(266, void, png_write_frame_head, (png_structp png_ptr, +PNG_EXPORT(268, void, png_write_frame_head, (png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers, png_uint_32 width, png_uint_32 height, png_uint_32 x_offset, png_uint_32 y_offset, png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, png_byte blend_op)); -PNG_EXPORT(267, void, png_write_frame_tail, (png_structp png_ptr, +PNG_EXPORT(269, void, png_write_frame_tail, (png_structp png_ptr, png_infop info_ptr)); #endif /* WRITE_APNG */ #endif /* APNG */ diff --git a/contrib/python/click/py3/.dist-info/METADATA b/contrib/python/click/py3/.dist-info/METADATA index 7a6bbb24b5..366d1a7e4f 100644 --- a/contrib/python/click/py3/.dist-info/METADATA +++ b/contrib/python/click/py3/.dist-info/METADATA @@ -1,30 +1,25 @@ -Metadata-Version: 2.1 +Metadata-Version: 2.3 Name: click -Version: 8.1.7 +Version: 8.1.8 Summary: Composable command line interface toolkit -Home-page: https://palletsprojects.com/p/click/ -Maintainer: Pallets -Maintainer-email: contact@palletsprojects.com -License: BSD-3-Clause -Project-URL: Donate, https://palletsprojects.com/donate -Project-URL: Documentation, https://click.palletsprojects.com/ -Project-URL: Changes, https://click.palletsprojects.com/changes/ -Project-URL: Source Code, https://github.com/pallets/click/ -Project-URL: Issue Tracker, https://github.com/pallets/click/issues/ -Project-URL: Chat, https://discord.gg/pallets +Maintainer-email: Pallets <contact@palletsprojects.com> +Requires-Python: >=3.7 +Description-Content-Type: text/markdown Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python -Requires-Python: >=3.7 -Description-Content-Type: text/x-rst -License-File: LICENSE.rst -Requires-Dist: colorama ; platform_system == "Windows" -Requires-Dist: importlib-metadata ; python_version < "3.8" +Classifier: Typing :: Typed +Requires-Dist: colorama; platform_system == 'Windows' +Requires-Dist: importlib-metadata; python_version < '3.8' +Project-URL: Changes, https://click.palletsprojects.com/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/click/ -\$ click\_ -========== +# $ click_ Click is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary. It's the "Command @@ -42,62 +37,38 @@ Click in three points: - Supports lazy loading of subcommands at runtime -Installing ----------- +## A Simple Example -Install and update using `pip`_: +```python +import click -.. code-block:: text +@click.command() +@click.option("--count", default=1, help="Number of greetings.") +@click.option("--name", prompt="Your name", help="The person to greet.") +def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") - $ pip install -U click +if __name__ == '__main__': + hello() +``` -.. _pip: https://pip.pypa.io/en/stable/getting-started/ +``` +$ python hello.py --count=3 +Your name: Click +Hello, Click! +Hello, Click! +Hello, Click! +``` -A Simple Example ----------------- - -.. code-block:: python - - import click - - @click.command() - @click.option("--count", default=1, help="Number of greetings.") - @click.option("--name", prompt="Your name", help="The person to greet.") - def hello(count, name): - """Simple program that greets NAME for a total of COUNT times.""" - for _ in range(count): - click.echo(f"Hello, {name}!") - - if __name__ == '__main__': - hello() - -.. code-block:: text - - $ python hello.py --count=3 - Your name: Click - Hello, Click! - Hello, Click! - Hello, Click! - - -Donate ------- +## Donate The Pallets organization develops and supports Click and other popular packages. In order to grow the community of contributors and users, and -allow the maintainers to devote more time to the projects, `please -donate today`_. - -.. _please donate today: https://palletsprojects.com/donate - +allow the maintainers to devote more time to the projects, [please +donate today][]. -Links ------ +[please donate today]: https://palletsprojects.com/donate -- Documentation: https://click.palletsprojects.com/ -- Changes: https://click.palletsprojects.com/changes/ -- PyPI Releases: https://pypi.org/project/click/ -- Source Code: https://github.com/pallets/click -- Issue Tracker: https://github.com/pallets/click/issues -- Chat: https://discord.gg/pallets diff --git a/contrib/python/click/py3/LICENSE.rst b/contrib/python/click/py3/LICENSE.txt index d12a849186..d12a849186 100644 --- a/contrib/python/click/py3/LICENSE.rst +++ b/contrib/python/click/py3/LICENSE.txt diff --git a/contrib/python/click/py3/README.md b/contrib/python/click/py3/README.md new file mode 100644 index 0000000000..1aa055dc04 --- /dev/null +++ b/contrib/python/click/py3/README.md @@ -0,0 +1,52 @@ +# $ click_ + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +## A Simple Example + +```python +import click + +@click.command() +@click.option("--count", default=1, help="Number of greetings.") +@click.option("--name", prompt="Your name", help="The person to greet.") +def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + +if __name__ == '__main__': + hello() +``` + +``` +$ python hello.py --count=3 +Your name: Click +Hello, Click! +Hello, Click! +Hello, Click! +``` + + +## Donate + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate diff --git a/contrib/python/click/py3/README.rst b/contrib/python/click/py3/README.rst deleted file mode 100644 index 76f26978a6..0000000000 --- a/contrib/python/click/py3/README.rst +++ /dev/null @@ -1,78 +0,0 @@ -\$ click\_ -========== - -Click is a Python package for creating beautiful command line interfaces -in a composable way with as little code as necessary. It's the "Command -Line Interface Creation Kit". It's highly configurable but comes with -sensible defaults out of the box. - -It aims to make the process of writing command line tools quick and fun -while also preventing any frustration caused by the inability to -implement an intended CLI API. - -Click in three points: - -- Arbitrary nesting of commands -- Automatic help page generation -- Supports lazy loading of subcommands at runtime - - -Installing ----------- - -Install and update using `pip`_: - -.. code-block:: text - - $ pip install -U click - -.. _pip: https://pip.pypa.io/en/stable/getting-started/ - - -A Simple Example ----------------- - -.. code-block:: python - - import click - - @click.command() - @click.option("--count", default=1, help="Number of greetings.") - @click.option("--name", prompt="Your name", help="The person to greet.") - def hello(count, name): - """Simple program that greets NAME for a total of COUNT times.""" - for _ in range(count): - click.echo(f"Hello, {name}!") - - if __name__ == '__main__': - hello() - -.. code-block:: text - - $ python hello.py --count=3 - Your name: Click - Hello, Click! - Hello, Click! - Hello, Click! - - -Donate ------- - -The Pallets organization develops and supports Click and other popular -packages. In order to grow the community of contributors and users, and -allow the maintainers to devote more time to the projects, `please -donate today`_. - -.. _please donate today: https://palletsprojects.com/donate - - -Links ------ - -- Documentation: https://click.palletsprojects.com/ -- Changes: https://click.palletsprojects.com/changes/ -- PyPI Releases: https://pypi.org/project/click/ -- Source Code: https://github.com/pallets/click -- Issue Tracker: https://github.com/pallets/click/issues -- Chat: https://discord.gg/pallets diff --git a/contrib/python/click/py3/click/__init__.py b/contrib/python/click/py3/click/__init__.py index 9a1dab0489..2610d0e142 100644 --- a/contrib/python/click/py3/click/__init__.py +++ b/contrib/python/click/py3/click/__init__.py @@ -4,6 +4,7 @@ writing command line scripts fun. Unlike other modules, it's based around a simple API that does not come with too much magic and is composable. """ + from .core import Argument as Argument from .core import BaseCommand as BaseCommand from .core import Command as Command @@ -18,6 +19,7 @@ from .decorators import command as command from .decorators import confirmation_option as confirmation_option from .decorators import group as group from .decorators import help_option as help_option +from .decorators import HelpOption as HelpOption from .decorators import make_pass_decorator as make_pass_decorator from .decorators import option as option from .decorators import pass_context as pass_context @@ -70,4 +72,4 @@ from .utils import get_binary_stream as get_binary_stream from .utils import get_text_stream as get_text_stream from .utils import open_file as open_file -__version__ = "8.1.7" +__version__ = "8.1.8" diff --git a/contrib/python/click/py3/click/_compat.py b/contrib/python/click/py3/click/_compat.py index 23f8866598..9153d150ce 100644 --- a/contrib/python/click/py3/click/_compat.py +++ b/contrib/python/click/py3/click/_compat.py @@ -516,7 +516,7 @@ if sys.platform.startswith("win") and WIN: _ansi_stream_wrappers: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() - def auto_wrap_for_ansi( # noqa: F811 + def auto_wrap_for_ansi( stream: t.TextIO, color: t.Optional[bool] = None ) -> t.TextIO: """Support ANSI color and style codes on Windows by wrapping a diff --git a/contrib/python/click/py3/click/_termui_impl.py b/contrib/python/click/py3/click/_termui_impl.py index f744657753..ad9f8f6c93 100644 --- a/contrib/python/click/py3/click/_termui_impl.py +++ b/contrib/python/click/py3/click/_termui_impl.py @@ -3,6 +3,7 @@ This module contains implementations for the termui module. To keep the import time of Click down, some infrequently used functionality is placed in this module and only imported as needed. """ + import contextlib import math import os @@ -11,6 +12,7 @@ import time import typing as t from gettext import gettext as _ from io import StringIO +from shutil import which from types import TracebackType from ._compat import _default_text_stdout @@ -371,31 +373,42 @@ def pager(generator: t.Iterable[str], color: t.Optional[bool] = None) -> None: pager_cmd = (os.environ.get("PAGER", None) or "").strip() if pager_cmd: if WIN: - return _tempfilepager(generator, pager_cmd, color) - return _pipepager(generator, pager_cmd, color) + if _tempfilepager(generator, pager_cmd, color): + return + elif _pipepager(generator, pager_cmd, color): + return if os.environ.get("TERM") in ("dumb", "emacs"): return _nullpager(stdout, generator, color) - if WIN or sys.platform.startswith("os2"): - return _tempfilepager(generator, "more <", color) - if hasattr(os, "system") and os.system("(less) 2>/dev/null") == 0: - return _pipepager(generator, "less", color) + if (WIN or sys.platform.startswith("os2")) and _tempfilepager( + generator, "more", color + ): + return + if _pipepager(generator, "less", color): + return import tempfile fd, filename = tempfile.mkstemp() os.close(fd) try: - if hasattr(os, "system") and os.system(f'more "{filename}"') == 0: - return _pipepager(generator, "more", color) + if _pipepager(generator, "more", color): + return return _nullpager(stdout, generator, color) finally: os.unlink(filename) -def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> None: +def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> bool: """Page through text by feeding it to another program. Invoking a pager through this might support colors. + + Returns True if the command was found, False otherwise and thus another + pager should be attempted. """ + cmd_absolute = which(cmd) + if cmd_absolute is None: + return False + import subprocess env = dict(os.environ) @@ -411,19 +424,25 @@ def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> elif "r" in less_flags or "R" in less_flags: color = True - c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, env=env) - stdin = t.cast(t.BinaryIO, c.stdin) - encoding = get_best_encoding(stdin) + c = subprocess.Popen( + [cmd_absolute], + shell=True, + stdin=subprocess.PIPE, + env=env, + errors="replace", + text=True, + ) + assert c.stdin is not None try: for text in generator: if not color: text = strip_ansi(text) - stdin.write(text.encode(encoding, "replace")) + c.stdin.write(text) except (OSError, KeyboardInterrupt): pass else: - stdin.close() + c.stdin.close() # Less doesn't respect ^C, but catches it for its own UI purposes (aborting # search or other commands inside less). @@ -441,11 +460,25 @@ def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> else: break + return True + def _tempfilepager( - generator: t.Iterable[str], cmd: str, color: t.Optional[bool] -) -> None: - """Page through text by invoking a program on a temporary file.""" + generator: t.Iterable[str], + cmd: str, + color: t.Optional[bool], +) -> bool: + """Page through text by invoking a program on a temporary file. + + Returns True if the command was found, False otherwise and thus another + pager should be attempted. + """ + # Which is necessary for Windows, it is also recommended in the Popen docs. + cmd_absolute = which(cmd) + if cmd_absolute is None: + return False + + import subprocess import tempfile fd, filename = tempfile.mkstemp() @@ -457,11 +490,16 @@ def _tempfilepager( with open_stream(filename, "wb")[0] as f: f.write(text.encode(encoding)) try: - os.system(f'{cmd} "{filename}"') + subprocess.call([cmd_absolute, filename]) + except OSError: + # Command not found + pass finally: os.close(fd) os.unlink(filename) + return True + def _nullpager( stream: t.TextIO, generator: t.Iterable[str], color: t.Optional[bool] @@ -496,7 +534,7 @@ class Editor: if WIN: return "notepad" for editor in "sensible-editor", "vim", "nano": - if os.system(f"which {editor} >/dev/null 2>&1") == 0: + if which(editor) is not None: return editor return "vi" @@ -595,22 +633,33 @@ def open_url(url: str, wait: bool = False, locate: bool = False) -> int: null.close() elif WIN: if locate: - url = _unquote_file(url.replace('"', "")) - args = f'explorer /select,"{url}"' + url = _unquote_file(url) + args = ["explorer", f"/select,{url}"] else: - url = url.replace('"', "") - wait_str = "/WAIT" if wait else "" - args = f'start {wait_str} "" "{url}"' - return os.system(args) + args = ["start"] + if wait: + args.append("/WAIT") + args.append("") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 elif CYGWIN: if locate: - url = os.path.dirname(_unquote_file(url).replace('"', "")) - args = f'cygstart "{url}"' + url = _unquote_file(url) + args = ["cygstart", os.path.dirname(url)] else: - url = url.replace('"', "") - wait_str = "-w" if wait else "" - args = f'cygstart {wait_str} "{url}"' - return os.system(args) + args = ["cygstart"] + if wait: + args.append("-w") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 try: if locate: @@ -698,8 +747,8 @@ if WIN: return rv else: - import tty import termios + import tty @contextlib.contextmanager def raw_terminal() -> t.Iterator[int]: diff --git a/contrib/python/click/py3/click/core.py b/contrib/python/click/py3/click/core.py index cc65e896bf..e6305011ad 100644 --- a/contrib/python/click/py3/click/core.py +++ b/contrib/python/click/py3/click/core.py @@ -39,6 +39,8 @@ from .utils import PacifyFlushWrapper if t.TYPE_CHECKING: import typing_extensions as te + + from .decorators import HelpOption from .shell_completion import CompletionItem F = t.TypeVar("F", bound=t.Callable[..., t.Any]) @@ -115,9 +117,16 @@ def iter_params_for_processing( invocation_order: t.Sequence["Parameter"], declaration_order: t.Sequence["Parameter"], ) -> t.List["Parameter"]: - """Given a sequence of parameters in the order as should be considered - for processing and an iterable of parameters that exist, this returns - a list in the correct order as they should be processed. + """Returns all declared parameters in the order they should be processed. + + The declared parameters are re-shuffled depending on the order in which + they were invoked, as well as the eagerness of each parameters. + + The invocation order takes precedence over the declaration order. I.e. the + order in which the user provided them to the CLI is respected. + + This behavior and its effect on callback evaluation is detailed at: + https://click.palletsprojects.com/en/stable/advanced/#callback-evaluation-order """ def sort_key(item: "Parameter") -> t.Tuple[bool, float]: @@ -383,9 +392,9 @@ class Context: #: An optional normalization function for tokens. This is #: options, choices, commands etc. - self.token_normalize_func: t.Optional[ - t.Callable[[str], str] - ] = token_normalize_func + self.token_normalize_func: t.Optional[t.Callable[[str], str]] = ( + token_normalize_func + ) #: Indicates if resilient parsing is enabled. In that case Click #: will do its best to not cause any failures and default values @@ -624,7 +633,7 @@ class Context: def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: """Finds the closest object of a given type.""" - node: t.Optional["Context"] = self + node: t.Optional[Context] = self while node is not None: if isinstance(node.obj, object_type): @@ -646,14 +655,12 @@ class Context: @t.overload def lookup_default( self, name: str, call: "te.Literal[True]" = True - ) -> t.Optional[t.Any]: - ... + ) -> t.Optional[t.Any]: ... @t.overload def lookup_default( self, name: str, call: "te.Literal[False]" = ... - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: - ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: """Get the default for a parameter from :attr:`default_map`. @@ -713,24 +720,22 @@ class Context: @t.overload def invoke( - __self, # noqa: B902 + __self, __callback: "t.Callable[..., V]", *args: t.Any, **kwargs: t.Any, - ) -> V: - ... + ) -> V: ... @t.overload def invoke( - __self, # noqa: B902 + __self, __callback: "Command", *args: t.Any, **kwargs: t.Any, - ) -> t.Any: - ... + ) -> t.Any: ... def invoke( - __self, # noqa: B902 + __self, __callback: t.Union["Command", "t.Callable[..., V]"], *args: t.Any, **kwargs: t.Any, @@ -782,9 +787,7 @@ class Context: with ctx: return __callback(*args, **kwargs) - def forward( - __self, __cmd: "Command", *args: t.Any, **kwargs: t.Any # noqa: B902 - ) -> t.Any: + def forward(__self, __cmd: "Command", *args: t.Any, **kwargs: t.Any) -> t.Any: """Similar to :meth:`invoke` but fills in default keyword arguments from the current context if the other command expects it. This cannot invoke callbacks directly, only other commands. @@ -936,7 +939,10 @@ class BaseCommand: extra[key] = value ctx = self.context_class( - self, info_name=info_name, parent=parent, **extra # type: ignore + self, # type: ignore[arg-type] + info_name=info_name, + parent=parent, + **extra, ) with ctx.scope(cleanup=False): @@ -971,7 +977,7 @@ class BaseCommand: """ from click.shell_completion import CompletionItem - results: t.List["CompletionItem"] = [] + results: t.List[CompletionItem] = [] while ctx.parent is not None: ctx = ctx.parent @@ -993,8 +999,7 @@ class BaseCommand: complete_var: t.Optional[str] = None, standalone_mode: "te.Literal[True]" = True, **extra: t.Any, - ) -> "te.NoReturn": - ... + ) -> "te.NoReturn": ... @t.overload def main( @@ -1004,8 +1009,7 @@ class BaseCommand: complete_var: t.Optional[str] = None, standalone_mode: bool = ..., **extra: t.Any, - ) -> t.Any: - ... + ) -> t.Any: ... def main( self, @@ -1221,12 +1225,13 @@ class Command(BaseCommand): #: the list of parameters for this command in the order they #: should show up in the help page and execute. Eager parameters #: will automatically be handled before non eager ones. - self.params: t.List["Parameter"] = params or [] + self.params: t.List[Parameter] = params or [] self.help = help self.epilog = epilog self.options_metavar = options_metavar self.short_help = short_help self.add_help_option = add_help_option + self._help_option: t.Optional[HelpOption] = None self.no_args_is_help = no_args_is_help self.hidden = hidden self.deprecated = deprecated @@ -1289,25 +1294,29 @@ class Command(BaseCommand): return list(all_names) def get_help_option(self, ctx: Context) -> t.Optional["Option"]: - """Returns the help option object.""" + """Returns the help option object. + + Unless ``add_help_option`` is ``False``. + + .. versionchanged:: 8.1.8 + The help option is now cached to avoid creating it multiple times. + """ help_options = self.get_help_option_names(ctx) if not help_options or not self.add_help_option: return None - def show_help(ctx: Context, param: "Parameter", value: str) -> None: - if value and not ctx.resilient_parsing: - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - - return Option( - help_options, - is_flag=True, - is_eager=True, - expose_value=False, - callback=show_help, - help=_("Show this message and exit."), - ) + # Cache the help option object in private _help_option attribute to + # avoid creating it multiple times. Not doing this will break the + # callback odering by iter_params_for_processing(), which relies on + # object comparison. + if self._help_option is None: + # Avoid circular import. + from .decorators import HelpOption + + self._help_option = HelpOption(help_options) + + return self._help_option def make_parser(self, ctx: Context) -> OptionParser: """Creates the underlying option parser for this command.""" @@ -1444,7 +1453,7 @@ class Command(BaseCommand): """ from click.shell_completion import CompletionItem - results: t.List["CompletionItem"] = [] + results: t.List[CompletionItem] = [] if incomplete and not incomplete[0].isalnum(): for param in self.get_params(ctx): @@ -1604,7 +1613,7 @@ class MultiCommand(Command): return f(inner, *args, **kwargs) self._result_callback = rv = update_wrapper(t.cast(F, function), f) - return rv + return rv # type: ignore[return-value] return decorator @@ -1843,14 +1852,12 @@ class Group(MultiCommand): self.commands[name] = cmd @t.overload - def command(self, __func: t.Callable[..., t.Any]) -> Command: - ... + def command(self, __func: t.Callable[..., t.Any]) -> Command: ... @t.overload def command( self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], Command]: - ... + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: ... def command( self, *args: t.Any, **kwargs: t.Any @@ -1894,14 +1901,12 @@ class Group(MultiCommand): return decorator @t.overload - def group(self, __func: t.Callable[..., t.Any]) -> "Group": - ... + def group(self, __func: t.Callable[..., t.Any]) -> "Group": ... @t.overload def group( self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: - ... + ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: ... def group( self, *args: t.Any, **kwargs: t.Any @@ -2227,14 +2232,12 @@ class Parameter: @t.overload def get_default( self, ctx: Context, call: "te.Literal[True]" = True - ) -> t.Optional[t.Any]: - ... + ) -> t.Optional[t.Any]: ... @t.overload def get_default( self, ctx: Context, call: bool = ... - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: - ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... def get_default( self, ctx: Context, call: bool = True @@ -2681,7 +2684,9 @@ class Option(Parameter): if name is None: if not expose_value: return None, opts, secondary_opts - raise TypeError("Could not determine name for option") + raise TypeError( + f"Could not determine name for option with declarations {decls!r}" + ) if not opts and not secondary_opts: raise TypeError( @@ -2810,10 +2815,12 @@ class Option(Parameter): # For boolean flags that have distinct True/False opts, # use the opt without prefix instead of the value. default_string = split_opt( - (self.opts if self.default else self.secondary_opts)[0] + (self.opts if default_value else self.secondary_opts)[0] )[1] elif self.is_bool_flag and not self.secondary_opts and not default_value: default_string = "" + elif default_value == "": + default_string = '""' else: default_string = str(default_value) @@ -2842,14 +2849,12 @@ class Option(Parameter): @t.overload def get_default( self, ctx: Context, call: "te.Literal[True]" = True - ) -> t.Optional[t.Any]: - ... + ) -> t.Optional[t.Any]: ... @t.overload def get_default( self, ctx: Context, call: bool = ... - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: - ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... def get_default( self, ctx: Context, call: bool = True @@ -3021,7 +3026,7 @@ class Argument(Parameter): if not decls: if not expose_value: return None, [], [] - raise TypeError("Could not determine name for argument") + raise TypeError("Argument is marked as exposed, but does not have a name.") if len(decls) == 1: name = arg = decls[0] name = name.replace("-", "_").lower() diff --git a/contrib/python/click/py3/click/decorators.py b/contrib/python/click/py3/click/decorators.py index d9bba9502c..bcf8906e70 100644 --- a/contrib/python/click/py3/click/decorators.py +++ b/contrib/python/click/py3/click/decorators.py @@ -93,7 +93,7 @@ def make_pass_decorator( return update_wrapper(new_func, f) - return decorator # type: ignore[return-value] + return decorator def pass_meta_key( @@ -126,7 +126,7 @@ def pass_meta_key( f"Decorator that passes {doc_description} as the first argument" " to the decorated function." ) - return decorator # type: ignore[return-value] + return decorator CmdType = t.TypeVar("CmdType", bound=Command) @@ -134,8 +134,7 @@ CmdType = t.TypeVar("CmdType", bound=Command) # variant: no call, directly as decorator for a function. @t.overload -def command(name: _AnyCallable) -> Command: - ... +def command(name: _AnyCallable) -> Command: ... # variant: with positional name and with positional or keyword cls argument: @@ -145,8 +144,7 @@ def command( name: t.Optional[str], cls: t.Type[CmdType], **attrs: t.Any, -) -> t.Callable[[_AnyCallable], CmdType]: - ... +) -> t.Callable[[_AnyCallable], CmdType]: ... # variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) @@ -156,16 +154,14 @@ def command( *, cls: t.Type[CmdType], **attrs: t.Any, -) -> t.Callable[[_AnyCallable], CmdType]: - ... +) -> t.Callable[[_AnyCallable], CmdType]: ... # variant: with optional string name, no cls argument provided. @t.overload def command( name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any -) -> t.Callable[[_AnyCallable], Command]: - ... +) -> t.Callable[[_AnyCallable], Command]: ... def command( @@ -255,8 +251,7 @@ GrpType = t.TypeVar("GrpType", bound=Group) # variant: no call, directly as decorator for a function. @t.overload -def group(name: _AnyCallable) -> Group: - ... +def group(name: _AnyCallable) -> Group: ... # variant: with positional name and with positional or keyword cls argument: @@ -266,8 +261,7 @@ def group( name: t.Optional[str], cls: t.Type[GrpType], **attrs: t.Any, -) -> t.Callable[[_AnyCallable], GrpType]: - ... +) -> t.Callable[[_AnyCallable], GrpType]: ... # variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) @@ -277,16 +271,14 @@ def group( *, cls: t.Type[GrpType], **attrs: t.Any, -) -> t.Callable[[_AnyCallable], GrpType]: - ... +) -> t.Callable[[_AnyCallable], GrpType]: ... # variant: with optional string name, no cls argument provided. @t.overload def group( name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any -) -> t.Callable[[_AnyCallable], Group]: - ... +) -> t.Callable[[_AnyCallable], Group]: ... def group( @@ -495,7 +487,7 @@ def version_option( metadata: t.Optional[types.ModuleType] try: - from importlib import metadata # type: ignore + from importlib import metadata except ImportError: # Python < 3.8 import importlib_metadata as metadata # type: ignore @@ -530,32 +522,41 @@ def version_option( return option(*param_decls, **kwargs) -def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Add a ``--help`` option which immediately prints the help page +class HelpOption(Option): + """Pre-configured ``--help`` option which immediately prints the help page and exits the program. + """ - This is usually unnecessary, as the ``--help`` option is added to - each command automatically unless ``add_help_option=False`` is - passed. + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + **kwargs: t.Any, + ) -> None: + if not param_decls: + param_decls = ("--help",) - :param param_decls: One or more option names. Defaults to the single - value ``"--help"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs.setdefault("callback", self.show_help) - def callback(ctx: Context, param: Parameter, value: bool) -> None: - if not value or ctx.resilient_parsing: - return + super().__init__(param_decls, **kwargs) - echo(ctx.get_help(), color=ctx.color) - ctx.exit() + @staticmethod + def show_help(ctx: Context, param: Parameter, value: bool) -> None: + """Callback that print the help page on ``<stdout>`` and exits.""" + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() - if not param_decls: - param_decls = ("--help",) - kwargs.setdefault("is_flag", True) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("is_eager", True) - kwargs.setdefault("help", _("Show this message and exit.")) - kwargs["callback"] = callback +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Decorator for the pre-configured ``--help`` option defined above. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + kwargs.setdefault("cls", HelpOption) return option(*param_decls, **kwargs) diff --git a/contrib/python/click/py3/click/exceptions.py b/contrib/python/click/py3/click/exceptions.py index fe68a3613f..0b8315166e 100644 --- a/contrib/python/click/py3/click/exceptions.py +++ b/contrib/python/click/py3/click/exceptions.py @@ -3,6 +3,7 @@ from gettext import gettext as _ from gettext import ngettext from ._compat import get_text_stderr +from .globals import resolve_color_default from .utils import echo from .utils import format_filename @@ -13,7 +14,7 @@ if t.TYPE_CHECKING: def _join_param_hints( - param_hint: t.Optional[t.Union[t.Sequence[str], str]] + param_hint: t.Optional[t.Union[t.Sequence[str], str]], ) -> t.Optional[str]: if param_hint is not None and not isinstance(param_hint, str): return " / ".join(repr(x) for x in param_hint) @@ -29,6 +30,9 @@ class ClickException(Exception): def __init__(self, message: str) -> None: super().__init__(message) + # The context will be removed by the time we print the message, so cache + # the color settings here to be used later on (in `show`) + self.show_color: t.Optional[bool] = resolve_color_default() self.message = message def format_message(self) -> str: @@ -41,7 +45,11 @@ class ClickException(Exception): if file is None: file = get_text_stderr() - echo(_("Error: {message}").format(message=self.format_message()), file=file) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=self.show_color, + ) class UsageError(ClickException): @@ -58,7 +66,7 @@ class UsageError(ClickException): def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None: super().__init__(message) self.ctx = ctx - self.cmd: t.Optional["Command"] = self.ctx.command if self.ctx else None + self.cmd: t.Optional[Command] = self.ctx.command if self.ctx else None def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: if file is None: diff --git a/contrib/python/click/py3/click/globals.py b/contrib/python/click/py3/click/globals.py index 480058f10d..191e712dbd 100644 --- a/contrib/python/click/py3/click/globals.py +++ b/contrib/python/click/py3/click/globals.py @@ -3,19 +3,18 @@ from threading import local if t.TYPE_CHECKING: import typing_extensions as te + from .core import Context _local = local() @t.overload -def get_current_context(silent: "te.Literal[False]" = False) -> "Context": - ... +def get_current_context(silent: "te.Literal[False]" = False) -> "Context": ... @t.overload -def get_current_context(silent: bool = ...) -> t.Optional["Context"]: - ... +def get_current_context(silent: bool = ...) -> t.Optional["Context"]: ... def get_current_context(silent: bool = False) -> t.Optional["Context"]: diff --git a/contrib/python/click/py3/click/parser.py b/contrib/python/click/py3/click/parser.py index 5fa7adfac8..600b8436d7 100644 --- a/contrib/python/click/py3/click/parser.py +++ b/contrib/python/click/py3/click/parser.py @@ -17,6 +17,7 @@ by the Python Software Foundation. This is limited to code in parser.py. Copyright 2001-2006 Gregory P. Ward. All rights reserved. Copyright 2002-2006 Python Software Foundation. All rights reserved. """ + # This code uses parts of optparse written by Gregory P. Ward and # maintained by the Python Software Foundation. # Copyright 2001-2006 Gregory P. Ward @@ -33,6 +34,7 @@ from .exceptions import UsageError if t.TYPE_CHECKING: import typing_extensions as te + from .core import Argument as CoreArgument from .core import Context from .core import Option as CoreOption @@ -247,7 +249,7 @@ class ParsingState: self.opts: t.Dict[str, t.Any] = {} self.largs: t.List[str] = [] self.rargs = rargs - self.order: t.List["CoreParameter"] = [] + self.order: t.List[CoreParameter] = [] class OptionParser: diff --git a/contrib/python/click/py3/click/shell_completion.py b/contrib/python/click/py3/click/shell_completion.py index dc9e00b9b0..07d0f09bac 100644 --- a/contrib/python/click/py3/click/shell_completion.py +++ b/contrib/python/click/py3/click/shell_completion.py @@ -303,12 +303,19 @@ class BashComplete(ShellComplete): @staticmethod def _check_version() -> None: + import shutil import subprocess - output = subprocess.run( - ["bash", "-c", 'echo "${BASH_VERSION}"'], stdout=subprocess.PIPE - ) - match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) + bash_exe = shutil.which("bash") + + if bash_exe is None: + match = None + else: + output = subprocess.run( + [bash_exe, "--norc", "-c", 'echo "${BASH_VERSION}"'], + stdout=subprocess.PIPE, + ) + match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) if match is not None: major, minor = match.groups() diff --git a/contrib/python/click/py3/click/termui.py b/contrib/python/click/py3/click/termui.py index db7a4b2861..c084f19652 100644 --- a/contrib/python/click/py3/click/termui.py +++ b/contrib/python/click/py3/click/termui.py @@ -173,7 +173,7 @@ def prompt( if hide_input: echo(_("Error: The value you entered was invalid."), err=err) else: - echo(_("Error: {e.message}").format(e=e), err=err) # noqa: B306 + echo(_("Error: {e.message}").format(e=e), err=err) continue if not confirmation_prompt: return result diff --git a/contrib/python/click/py3/click/testing.py b/contrib/python/click/py3/click/testing.py index e0df0d2a65..772b2159cc 100644 --- a/contrib/python/click/py3/click/testing.py +++ b/contrib/python/click/py3/click/testing.py @@ -8,6 +8,7 @@ import tempfile import typing as t from types import TracebackType +from . import _compat from . import formatting from . import termui from . import utils @@ -311,10 +312,12 @@ class CliRunner: old_hidden_prompt_func = termui.hidden_prompt_func old__getchar_func = termui._getchar old_should_strip_ansi = utils.should_strip_ansi # type: ignore + old__compat_should_strip_ansi = _compat.should_strip_ansi termui.visible_prompt_func = visible_input termui.hidden_prompt_func = hidden_input termui._getchar = _getchar utils.should_strip_ansi = should_strip_ansi # type: ignore + _compat.should_strip_ansi = should_strip_ansi old_env = {} try: @@ -344,6 +347,7 @@ class CliRunner: termui.hidden_prompt_func = old_hidden_prompt_func termui._getchar = old__getchar_func utils.should_strip_ansi = old_should_strip_ansi # type: ignore + _compat.should_strip_ansi = old__compat_should_strip_ansi formatting.FORCED_WIDTH = old_forced_width def invoke( @@ -475,5 +479,5 @@ class CliRunner: if temp_dir is None: try: shutil.rmtree(dt) - except OSError: # noqa: B014 + except OSError: pass diff --git a/contrib/python/click/py3/click/types.py b/contrib/python/click/py3/click/types.py index 2b1d1797f2..a70fd58ce2 100644 --- a/contrib/python/click/py3/click/types.py +++ b/contrib/python/click/py3/click/types.py @@ -15,6 +15,7 @@ from .utils import safecall if t.TYPE_CHECKING: import typing_extensions as te + from .core import Context from .core import Parameter from .shell_completion import CompletionItem @@ -658,12 +659,15 @@ class File(ParamType): will not be held open until first IO. lazy is mainly useful when opening for writing to avoid creating the file until it is needed. - Starting with Click 2.0, files can also be opened atomically in which - case all writes go into a separate file in the same folder and upon - completion the file will be moved over to the original location. This - is useful if a file regularly read by other users is modified. + Files can also be opened atomically in which case all writes go into a + separate file in the same folder and upon completion the file will + be moved over to the original location. This is useful if a file + regularly read by other users is modified. See :ref:`file-args` for more information. + + .. versionchanged:: 2.0 + Added the ``atomic`` parameter. """ name = "filename" @@ -737,7 +741,7 @@ class File(ParamType): ctx.call_on_close(safecall(f.flush)) return f - except OSError as e: # noqa: B014 + except OSError as e: self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) def shell_complete( @@ -891,7 +895,7 @@ class Path(ParamType): ) if not self.dir_okay and stat.S_ISDIR(st.st_mode): self.fail( - _("{name} '{filename}' is a directory.").format( + _("{name} {filename!r} is a directory.").format( name=self.name.title(), filename=format_filename(value) ), param, diff --git a/contrib/python/click/py3/click/utils.py b/contrib/python/click/py3/click/utils.py index d536434f0b..836c6f21a0 100644 --- a/contrib/python/click/py3/click/utils.py +++ b/contrib/python/click/py3/click/utils.py @@ -156,7 +156,7 @@ class LazyFile: rv, self.should_close = open_stream( self.name, self.mode, self.encoding, self.errors, atomic=self.atomic ) - except OSError as e: # noqa: E402 + except OSError as e: from .exceptions import FileError raise FileError(self.name, hint=e.strerror) from e @@ -311,7 +311,7 @@ def echo( out = strip_ansi(out) elif WIN: if auto_wrap_for_ansi is not None: - file = auto_wrap_for_ansi(file) # type: ignore + file = auto_wrap_for_ansi(file, color) # type: ignore elif not color: out = strip_ansi(out) @@ -353,7 +353,7 @@ def get_text_stream( def open_file( - filename: str, + filename: t.Union[str, "os.PathLike[str]"], mode: str = "r", encoding: t.Optional[str] = None, errors: t.Optional[str] = "strict", @@ -374,7 +374,7 @@ def open_file( with open_file(filename) as f: ... - :param filename: The name of the file to open, or ``'-'`` for + :param filename: The name or Path of the file to open, or ``'-'`` for ``stdin``/``stdout``. :param mode: The mode in which to open the file. :param encoding: The encoding to decode or encode a file opened in @@ -410,7 +410,7 @@ def format_filename( with the replacement character ``�``. Invalid bytes or surrogate escapes will raise an error when written to a - stream with ``errors="strict". This will typically happen with ``stdout`` + stream with ``errors="strict"``. This will typically happen with ``stdout`` when the locale is something like ``en_GB.UTF-8``. Many scenarios *are* safe to write surrogates though, due to PEP 538 and diff --git a/contrib/python/click/py3/patches/01-fix-tests.patch b/contrib/python/click/py3/patches/01-fix-tests.patch index 5851660555..826b504434 100644 --- a/contrib/python/click/py3/patches/01-fix-tests.patch +++ b/contrib/python/click/py3/patches/01-fix-tests.patch @@ -36,7 +36,7 @@ --- contrib/python/click/py3/tests/test_utils.py (index) +++ contrib/python/click/py3/tests/test_utils.py (working tree) @@ -460,2 +460,2 @@ def test_expand_args(monkeypatch): -- assert "setup.cfg" in click.utils._expand_args(["*.cfg"]) +- assert "pyproject.toml" in click.utils._expand_args(["*.toml"]) - assert os.path.join("docs", "conf.py") in click.utils._expand_args(["**/conf.py"]) -+ #assert "setup.cfg" in click.utils._expand_args(["*.cfg"]) ++ #assert "pyproject.toml" in click.utils._expand_args(["*.toml"]) + #assert os.path.join("docs", "conf.py") in click.utils._expand_args(["**/conf.py"]) diff --git a/contrib/python/click/py3/tests/test_commands.py b/contrib/python/click/py3/tests/test_commands.py index ed9d96f3c5..dcf66acefb 100644 --- a/contrib/python/click/py3/tests/test_commands.py +++ b/contrib/python/click/py3/tests/test_commands.py @@ -305,6 +305,138 @@ def test_group_add_command_name(runner): assert result.exit_code == 0 +@pytest.mark.parametrize( + ("invocation_order", "declaration_order", "expected_order"), + [ + # Non-eager options. + ([], ["-a"], ["-a"]), + (["-a"], ["-a"], ["-a"]), + ([], ["-a", "-c"], ["-a", "-c"]), + (["-a"], ["-a", "-c"], ["-a", "-c"]), + (["-c"], ["-a", "-c"], ["-c", "-a"]), + ([], ["-c", "-a"], ["-c", "-a"]), + (["-a"], ["-c", "-a"], ["-a", "-c"]), + (["-c"], ["-c", "-a"], ["-c", "-a"]), + (["-a", "-c"], ["-a", "-c"], ["-a", "-c"]), + (["-c", "-a"], ["-a", "-c"], ["-c", "-a"]), + # Eager options. + ([], ["-b"], ["-b"]), + (["-b"], ["-b"], ["-b"]), + ([], ["-b", "-d"], ["-b", "-d"]), + (["-b"], ["-b", "-d"], ["-b", "-d"]), + (["-d"], ["-b", "-d"], ["-d", "-b"]), + ([], ["-d", "-b"], ["-d", "-b"]), + (["-b"], ["-d", "-b"], ["-b", "-d"]), + (["-d"], ["-d", "-b"], ["-d", "-b"]), + (["-b", "-d"], ["-b", "-d"], ["-b", "-d"]), + (["-d", "-b"], ["-b", "-d"], ["-d", "-b"]), + # Mixed options. + ([], ["-a", "-b", "-c", "-d"], ["-b", "-d", "-a", "-c"]), + (["-a"], ["-a", "-b", "-c", "-d"], ["-b", "-d", "-a", "-c"]), + (["-b"], ["-a", "-b", "-c", "-d"], ["-b", "-d", "-a", "-c"]), + (["-c"], ["-a", "-b", "-c", "-d"], ["-b", "-d", "-c", "-a"]), + (["-d"], ["-a", "-b", "-c", "-d"], ["-d", "-b", "-a", "-c"]), + (["-a", "-b"], ["-a", "-b", "-c", "-d"], ["-b", "-d", "-a", "-c"]), + (["-b", "-a"], ["-a", "-b", "-c", "-d"], ["-b", "-d", "-a", "-c"]), + (["-d", "-c"], ["-a", "-b", "-c", "-d"], ["-d", "-b", "-c", "-a"]), + (["-c", "-d"], ["-a", "-b", "-c", "-d"], ["-d", "-b", "-c", "-a"]), + (["-a", "-b", "-c", "-d"], ["-a", "-b", "-c", "-d"], ["-b", "-d", "-a", "-c"]), + (["-b", "-d", "-a", "-c"], ["-a", "-b", "-c", "-d"], ["-b", "-d", "-a", "-c"]), + ([], ["-b", "-d", "-e", "-a", "-c"], ["-b", "-d", "-e", "-a", "-c"]), + (["-a", "-d"], ["-b", "-d", "-e", "-a", "-c"], ["-d", "-b", "-e", "-a", "-c"]), + (["-c", "-d"], ["-b", "-d", "-e", "-a", "-c"], ["-d", "-b", "-e", "-c", "-a"]), + ], +) +def test_iter_params_for_processing( + invocation_order, declaration_order, expected_order +): + parameters = { + "-a": click.Option(["-a"]), + "-b": click.Option(["-b"], is_eager=True), + "-c": click.Option(["-c"]), + "-d": click.Option(["-d"], is_eager=True), + "-e": click.Option(["-e"], is_eager=True), + } + + invocation_params = [parameters[opt_id] for opt_id in invocation_order] + declaration_params = [parameters[opt_id] for opt_id in declaration_order] + expected_params = [parameters[opt_id] for opt_id in expected_order] + + assert ( + click.core.iter_params_for_processing(invocation_params, declaration_params) + == expected_params + ) + + +def test_help_param_priority(runner): + """Cover the edge-case in which the eagerness of help option was not + respected, because it was internally generated multiple times. + + See: https://github.com/pallets/click/pull/2811 + """ + + def print_and_exit(ctx, param, value): + if value: + click.echo(f"Value of {param.name} is: {value}") + ctx.exit() + + @click.command(context_settings={"help_option_names": ("--my-help",)}) + @click.option("-a", is_flag=True, expose_value=False, callback=print_and_exit) + @click.option( + "-b", is_flag=True, expose_value=False, callback=print_and_exit, is_eager=True + ) + def cli(): + pass + + # --my-help is properly called and stop execution. + result = runner.invoke(cli, ["--my-help"]) + assert "Value of a is: True" not in result.stdout + assert "Value of b is: True" not in result.stdout + assert "--my-help" in result.stdout + assert result.exit_code == 0 + + # -a is properly called and stop execution. + result = runner.invoke(cli, ["-a"]) + assert "Value of a is: True" in result.stdout + assert "Value of b is: True" not in result.stdout + assert "--my-help" not in result.stdout + assert result.exit_code == 0 + + # -a takes precedence over -b and stop execution. + result = runner.invoke(cli, ["-a", "-b"]) + assert "Value of a is: True" not in result.stdout + assert "Value of b is: True" in result.stdout + assert "--my-help" not in result.stdout + assert result.exit_code == 0 + + # --my-help is eager by default so takes precedence over -a and stop + # execution, whatever the order. + for args in [["-a", "--my-help"], ["--my-help", "-a"]]: + result = runner.invoke(cli, args) + assert "Value of a is: True" not in result.stdout + assert "Value of b is: True" not in result.stdout + assert "--my-help" in result.stdout + assert result.exit_code == 0 + + # Both -b and --my-help are eager so they're called in the order they're + # invoked by the user. + result = runner.invoke(cli, ["-b", "--my-help"]) + assert "Value of a is: True" not in result.stdout + assert "Value of b is: True" in result.stdout + assert "--my-help" not in result.stdout + assert result.exit_code == 0 + + # But there was a bug when --my-help is called before -b, because the + # --my-help option created by click via help_option_names is internally + # created twice and is not the same object, breaking the priority order + # produced by iter_params_for_processing. + result = runner.invoke(cli, ["--my-help", "-b"]) + assert "Value of a is: True" not in result.stdout + assert "Value of b is: True" not in result.stdout + assert "--my-help" in result.stdout + assert result.exit_code == 0 + + def test_unprocessed_options(runner): @click.command(context_settings=dict(ignore_unknown_options=True)) @click.argument("args", nargs=-1, type=click.UNPROCESSED) diff --git a/contrib/python/click/py3/tests/test_defaults.py b/contrib/python/click/py3/tests/test_defaults.py index 8ef5ea2922..5c5e168ab6 100644 --- a/contrib/python/click/py3/tests/test_defaults.py +++ b/contrib/python/click/py3/tests/test_defaults.py @@ -5,7 +5,7 @@ def test_basic_defaults(runner): @click.command() @click.option("--foo", default=42, type=click.FLOAT) def cli(foo): - assert type(foo) is float + assert type(foo) is float # noqa E721 click.echo(f"FOO:[{foo}]") result = runner.invoke(cli, []) @@ -18,7 +18,7 @@ def test_multiple_defaults(runner): @click.option("--foo", default=[23, 42], type=click.FLOAT, multiple=True) def cli(foo): for item in foo: - assert type(item) is float + assert type(item) is float # noqa E721 click.echo(item) result = runner.invoke(cli, []) @@ -59,3 +59,28 @@ def test_multiple_flag_default(runner): result = runner.invoke(cli, ["-y", "-n", "-f", "-v", "-q"], standalone_mode=False) assert result.return_value == ((True, False), (True,), (1, -1)) + + +def test_flag_default_map(runner): + """test flag with default map""" + + @click.group() + def cli(): + pass + + @cli.command() + @click.option("--name/--no-name", is_flag=True, show_default=True, help="name flag") + def foo(name): + click.echo(name) + + result = runner.invoke(cli, ["foo"]) + assert "False" in result.output + + result = runner.invoke(cli, ["foo", "--help"]) + assert "default: no-name" in result.output + + result = runner.invoke(cli, ["foo"], default_map={"foo": {"name": True}}) + assert "True" in result.output + + result = runner.invoke(cli, ["foo", "--help"], default_map={"foo": {"name": True}}) + assert "default: name" in result.output diff --git a/contrib/python/click/py3/tests/test_imports.py b/contrib/python/click/py3/tests/test_imports.py index 38a214c873..d6724059d7 100644 --- a/contrib/python/click/py3/tests/test_imports.py +++ b/contrib/python/click/py3/tests/test_imports.py @@ -5,7 +5,6 @@ import sys from click._compat import WIN - IMPORT_TEST = b"""\ import builtins @@ -49,6 +48,7 @@ ALLOWED_IMPORTS = { "typing", "types", "gettext", + "shutil", } if WIN: diff --git a/contrib/python/click/py3/tests/test_normalization.py b/contrib/python/click/py3/tests/test_normalization.py index 32df098e9b..502e654a37 100644 --- a/contrib/python/click/py3/tests/test_normalization.py +++ b/contrib/python/click/py3/tests/test_normalization.py @@ -1,6 +1,5 @@ import click - CONTEXT_SETTINGS = dict(token_normalize_func=lambda x: x.lower()) diff --git a/contrib/python/click/py3/tests/test_options.py b/contrib/python/click/py3/tests/test_options.py index 91249b234f..7397f36676 100644 --- a/contrib/python/click/py3/tests/test_options.py +++ b/contrib/python/click/py3/tests/test_options.py @@ -786,6 +786,14 @@ def test_show_default_string(runner): assert "[default: (unlimited)]" in message +def test_show_default_with_empty_string(runner): + """When show_default is True and default is set to an empty string.""" + opt = click.Option(["--limit"], default="", show_default=True) + ctx = click.Context(click.Command("cli")) + message = opt.get_help_record(ctx)[1] + assert '[default: ""]' in message + + def test_do_not_show_no_default(runner): """When show_default is True and no default is set do not show None.""" opt = click.Option(["--limit"], show_default=True) diff --git a/contrib/python/click/py3/tests/test_testing.py b/contrib/python/click/py3/tests/test_testing.py index 9f294b3a13..0d227f2a0a 100644 --- a/contrib/python/click/py3/tests/test_testing.py +++ b/contrib/python/click/py3/tests/test_testing.py @@ -5,7 +5,7 @@ from io import BytesIO import pytest import click -from click._compat import WIN +from click.exceptions import ClickException from click.testing import CliRunner @@ -184,7 +184,6 @@ def test_catch_exceptions(): assert result.exit_code == 1 -@pytest.mark.skipif(WIN, reason="Test does not make sense on Windows.") def test_with_color(): @click.command() def cli(): @@ -201,6 +200,26 @@ def test_with_color(): assert not result.exception +def test_with_color_errors(): + class CLIError(ClickException): + def format_message(self) -> str: + return click.style(self.message, fg="red") + + @click.command() + def cli(): + raise CLIError("Red error") + + runner = CliRunner() + + result = runner.invoke(cli) + assert result.output == "Error: Red error\n" + assert result.exception + + result = runner.invoke(cli, color=True) + assert result.output == f"Error: {click.style('Red error', fg='red')}\n" + assert result.exception + + def test_with_color_but_pause_not_blocking(): @click.command() def cli(): @@ -321,7 +340,7 @@ def test_stderr(): assert result_mix.stdout == "stdout\nstderr\n" with pytest.raises(ValueError): - result_mix.stderr + result_mix.stderr # noqa B018 @click.command() def cli_empty_stderr(): diff --git a/contrib/python/click/py3/tests/test_types.py b/contrib/python/click/py3/tests/test_types.py index 0a2508882a..79068e1896 100644 --- a/contrib/python/click/py3/tests/test_types.py +++ b/contrib/python/click/py3/tests/test_types.py @@ -1,5 +1,6 @@ import os.path import pathlib +import platform import tempfile import pytest @@ -232,3 +233,14 @@ def test_file_surrogates(type, tmp_path): def test_file_error_surrogates(): message = FileError(filename="\udcff").format_message() assert message == "Could not open file '�': unknown error" + + +@pytest.mark.skipif( + platform.system() == "Windows", reason="Filepath syntax differences." +) +def test_invalid_path_with_esc_sequence(): + with pytest.raises(click.BadParameter) as exc_info: + with tempfile.TemporaryDirectory(prefix="my\ndir") as tempdir: + click.Path(dir_okay=False).convert(tempdir, None, None) + + assert "my\\ndir" in exc_info.value.message diff --git a/contrib/python/click/py3/tests/test_utils.py b/contrib/python/click/py3/tests/test_utils.py index 656d2f9e53..45433c6790 100644 --- a/contrib/python/click/py3/tests/test_utils.py +++ b/contrib/python/click/py3/tests/test_utils.py @@ -36,9 +36,7 @@ def test_echo(runner): def test_echo_custom_file(): - import io - - f = io.StringIO() + f = StringIO() click.echo("hello", file=f) assert f.getvalue() == "hello\n" @@ -181,7 +179,6 @@ def _test_gen_func(): yield "abc" -@pytest.mark.skipif(WIN, reason="Different behavior on windows.") @pytest.mark.parametrize("cat", ["cat", "cat ", "cat "]) @pytest.mark.parametrize( "test", @@ -209,7 +206,6 @@ def test_echo_via_pager(monkeypatch, capfd, cat, test): assert out == expected_output -@pytest.mark.skipif(WIN, reason="Test does not make sense on Windows.") def test_echo_color_flag(monkeypatch, capfd): isatty = True monkeypatch.setattr(click._compat, "isatty", lambda x: isatty) @@ -232,16 +228,23 @@ def test_echo_color_flag(monkeypatch, capfd): assert out == f"{styled_text}\n" isatty = False - click.echo(styled_text) - out, err = capfd.readouterr() - assert out == f"{text}\n" + # Faking isatty() is not enough on Windows; + # the implementation caches the colorama wrapped stream + # so we have to use a new stream for each test + stream = StringIO() + click.echo(styled_text, file=stream) + assert stream.getvalue() == f"{text}\n" + + stream = StringIO() + click.echo(styled_text, file=stream, color=True) + assert stream.getvalue() == f"{styled_text}\n" def test_prompt_cast_default(capfd, monkeypatch): monkeypatch.setattr(sys, "stdin", StringIO("\n")) value = click.prompt("value", default="100", type=int) capfd.readouterr() - assert type(value) is int + assert type(value) is int # noqa E721 @pytest.mark.skipif(WIN, reason="Test too complex to make work windows.") @@ -464,7 +467,7 @@ def test_expand_args(monkeypatch): assert user in click.utils._expand_args(["~"]) monkeypatch.setenv("CLICK_TEST", "hello") assert "hello" in click.utils._expand_args(["$CLICK_TEST"]) - #assert "setup.cfg" in click.utils._expand_args(["*.cfg"]) + #assert "pyproject.toml" in click.utils._expand_args(["*.toml"]) #assert os.path.join("docs", "conf.py") in click.utils._expand_args(["**/conf.py"]) assert "*.not-found" in click.utils._expand_args(["*.not-found"]) # a bad glob pattern, such as a pytest identifier, should return itself diff --git a/contrib/python/click/py3/tests/typing/typing_aliased_group.py b/contrib/python/click/py3/tests/typing/typing_aliased_group.py index ccc706bc53..a1fdac4b22 100644 --- a/contrib/python/click/py3/tests/typing/typing_aliased_group.py +++ b/contrib/python/click/py3/tests/typing/typing_aliased_group.py @@ -1,4 +1,5 @@ """Example from https://click.palletsprojects.com/en/8.1.x/advanced/#command-aliases""" + from __future__ import annotations from typing_extensions import assert_type diff --git a/contrib/python/click/py3/tests/typing/typing_confirmation_option.py b/contrib/python/click/py3/tests/typing/typing_confirmation_option.py index 09ecaa96da..a568a6ad06 100644 --- a/contrib/python/click/py3/tests/typing/typing_confirmation_option.py +++ b/contrib/python/click/py3/tests/typing/typing_confirmation_option.py @@ -1,4 +1,5 @@ """From https://click.palletsprojects.com/en/8.1.x/options/#yes-parameters""" + from typing_extensions import assert_type import click diff --git a/contrib/python/click/py3/tests/typing/typing_options.py b/contrib/python/click/py3/tests/typing/typing_options.py index 6a6611928e..613e8d43de 100644 --- a/contrib/python/click/py3/tests/typing/typing_options.py +++ b/contrib/python/click/py3/tests/typing/typing_options.py @@ -1,4 +1,5 @@ """From https://click.palletsprojects.com/en/8.1.x/quickstart/#adding-parameters""" + from typing_extensions import assert_type import click diff --git a/contrib/python/click/py3/tests/typing/typing_simple_example.py b/contrib/python/click/py3/tests/typing/typing_simple_example.py index 0a94b8cb78..641937c5dc 100644 --- a/contrib/python/click/py3/tests/typing/typing_simple_example.py +++ b/contrib/python/click/py3/tests/typing/typing_simple_example.py @@ -1,4 +1,5 @@ """The simple example from https://github.com/pallets/click#a-simple-example.""" + from typing_extensions import assert_type import click diff --git a/contrib/python/click/py3/tests/typing/typing_version_option.py b/contrib/python/click/py3/tests/typing/typing_version_option.py index fd473a001b..7ff37a75fb 100644 --- a/contrib/python/click/py3/tests/typing/typing_version_option.py +++ b/contrib/python/click/py3/tests/typing/typing_version_option.py @@ -1,6 +1,7 @@ """ From https://click.palletsprojects.com/en/8.1.x/options/#callbacks-and-eager-options. """ + from typing_extensions import assert_type import click diff --git a/contrib/python/click/py3/ya.make b/contrib/python/click/py3/ya.make index 2b33af1eb4..a71cf09a2c 100644 --- a/contrib/python/click/py3/ya.make +++ b/contrib/python/click/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(8.1.7) +VERSION(8.1.8) LICENSE(BSD-3-Clause) |