scitex 2.0.0__py2.py3-none-any.whl → 2.1.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scitex/__init__.py +53 -15
- scitex/__main__.py +72 -26
- scitex/__version__.py +1 -1
- scitex/_sh.py +145 -23
- scitex/ai/__init__.py +30 -16
- scitex/ai/_gen_ai/_Anthropic.py +5 -7
- scitex/ai/_gen_ai/_BaseGenAI.py +2 -2
- scitex/ai/_gen_ai/_DeepSeek.py +10 -2
- scitex/ai/_gen_ai/_Google.py +2 -2
- scitex/ai/_gen_ai/_Llama.py +2 -2
- scitex/ai/_gen_ai/_OpenAI.py +2 -2
- scitex/ai/_gen_ai/_PARAMS.py +51 -65
- scitex/ai/_gen_ai/_Perplexity.py +2 -2
- scitex/ai/_gen_ai/__init__.py +25 -14
- scitex/ai/_gen_ai/_format_output_func.py +4 -4
- scitex/ai/classification/{classifier_server.py → Classifier.py} +5 -5
- scitex/ai/classification/CrossValidationExperiment.py +374 -0
- scitex/ai/classification/__init__.py +43 -4
- scitex/ai/classification/reporters/_BaseClassificationReporter.py +281 -0
- scitex/ai/classification/reporters/_ClassificationReporter.py +773 -0
- scitex/ai/classification/reporters/_MultiClassificationReporter.py +406 -0
- scitex/ai/classification/reporters/_SingleClassificationReporter.py +1834 -0
- scitex/ai/classification/reporters/__init__.py +11 -0
- scitex/ai/classification/reporters/reporter_utils/_Plotter.py +1028 -0
- scitex/ai/classification/reporters/reporter_utils/__init__.py +80 -0
- scitex/ai/classification/reporters/reporter_utils/aggregation.py +457 -0
- scitex/ai/classification/reporters/reporter_utils/data_models.py +313 -0
- scitex/ai/classification/reporters/reporter_utils/reporting.py +1056 -0
- scitex/ai/classification/reporters/reporter_utils/storage.py +221 -0
- scitex/ai/classification/reporters/reporter_utils/validation.py +395 -0
- scitex/ai/classification/timeseries/_TimeSeriesBlockingSplit.py +568 -0
- scitex/ai/classification/timeseries/_TimeSeriesCalendarSplit.py +688 -0
- scitex/ai/classification/timeseries/_TimeSeriesMetadata.py +139 -0
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +1716 -0
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +1685 -0
- scitex/ai/classification/timeseries/_TimeSeriesStrategy.py +84 -0
- scitex/ai/classification/timeseries/_TimeSeriesStratifiedSplit.py +610 -0
- scitex/ai/classification/timeseries/__init__.py +39 -0
- scitex/ai/classification/timeseries/_normalize_timestamp.py +436 -0
- scitex/ai/clustering/_umap.py +2 -2
- scitex/ai/feature_extraction/vit.py +1 -0
- scitex/ai/feature_selection/__init__.py +30 -0
- scitex/ai/feature_selection/feature_selection.py +364 -0
- scitex/ai/loss/multi_task_loss.py +1 -1
- scitex/ai/metrics/__init__.py +51 -4
- scitex/ai/metrics/_calc_bacc.py +61 -0
- scitex/ai/metrics/_calc_bacc_from_conf_mat.py +38 -0
- scitex/ai/metrics/_calc_clf_report.py +78 -0
- scitex/ai/metrics/_calc_conf_mat.py +93 -0
- scitex/ai/metrics/_calc_feature_importance.py +183 -0
- scitex/ai/metrics/_calc_mcc.py +61 -0
- scitex/ai/metrics/_calc_pre_rec_auc.py +116 -0
- scitex/ai/metrics/_calc_roc_auc.py +110 -0
- scitex/ai/metrics/_calc_seizure_prediction_metrics.py +490 -0
- scitex/ai/metrics/{silhoute_score_block.py → _calc_silhouette_score.py} +15 -8
- scitex/ai/metrics/_normalize_labels.py +83 -0
- scitex/ai/plt/__init__.py +47 -8
- scitex/ai/plt/{_conf_mat.py → _plot_conf_mat.py} +158 -87
- scitex/ai/plt/_plot_feature_importance.py +323 -0
- scitex/ai/plt/_plot_learning_curve.py +345 -0
- scitex/ai/plt/_plot_optuna_study.py +225 -0
- scitex/ai/plt/_plot_pre_rec_curve.py +290 -0
- scitex/ai/plt/_plot_roc_curve.py +255 -0
- scitex/ai/training/{learning_curve_logger.py → _LearningCurveLogger.py} +197 -213
- scitex/ai/training/__init__.py +2 -2
- scitex/ai/utils/grid_search.py +3 -3
- scitex/benchmark/__init__.py +52 -0
- scitex/benchmark/benchmark.py +400 -0
- scitex/benchmark/monitor.py +370 -0
- scitex/benchmark/profiler.py +297 -0
- scitex/browser/__init__.py +48 -0
- scitex/browser/automation/CookieHandler.py +216 -0
- scitex/browser/automation/__init__.py +7 -0
- scitex/browser/collaboration/__init__.py +55 -0
- scitex/browser/collaboration/auth_helpers.py +94 -0
- scitex/browser/collaboration/collaborative_agent.py +136 -0
- scitex/browser/collaboration/credential_manager.py +188 -0
- scitex/browser/collaboration/interactive_panel.py +400 -0
- scitex/browser/collaboration/persistent_browser.py +170 -0
- scitex/browser/collaboration/shared_session.py +383 -0
- scitex/browser/collaboration/standard_interactions.py +246 -0
- scitex/browser/collaboration/visual_feedback.py +181 -0
- scitex/browser/core/BrowserMixin.py +326 -0
- scitex/browser/core/ChromeProfileManager.py +446 -0
- scitex/browser/core/__init__.py +9 -0
- scitex/browser/debugging/__init__.py +18 -0
- scitex/browser/debugging/_browser_logger.py +657 -0
- scitex/browser/debugging/_highlight_element.py +143 -0
- scitex/browser/debugging/_show_grid.py +154 -0
- scitex/browser/interaction/__init__.py +24 -0
- scitex/browser/interaction/click_center.py +149 -0
- scitex/browser/interaction/click_with_fallbacks.py +206 -0
- scitex/browser/interaction/close_popups.py +498 -0
- scitex/browser/interaction/fill_with_fallbacks.py +209 -0
- scitex/browser/pdf/__init__.py +14 -0
- scitex/browser/pdf/click_download_for_chrome_pdf_viewer.py +200 -0
- scitex/browser/pdf/detect_chrome_pdf_viewer.py +198 -0
- scitex/browser/remote/CaptchaHandler.py +434 -0
- scitex/browser/remote/ZenRowsAPIClient.py +347 -0
- scitex/browser/remote/ZenRowsBrowserManager.py +570 -0
- scitex/browser/remote/__init__.py +11 -0
- scitex/browser/stealth/HumanBehavior.py +344 -0
- scitex/browser/stealth/StealthManager.py +1008 -0
- scitex/browser/stealth/__init__.py +9 -0
- scitex/browser/template.py +122 -0
- scitex/capture/__init__.py +110 -0
- scitex/capture/__main__.py +25 -0
- scitex/capture/capture.py +848 -0
- scitex/capture/cli.py +233 -0
- scitex/capture/gif.py +344 -0
- scitex/capture/mcp_server.py +961 -0
- scitex/capture/session.py +70 -0
- scitex/capture/utils.py +705 -0
- scitex/cli/__init__.py +17 -0
- scitex/cli/cloud.py +447 -0
- scitex/cli/main.py +42 -0
- scitex/cli/scholar.py +280 -0
- scitex/context/_suppress_output.py +5 -3
- scitex/db/__init__.py +30 -3
- scitex/db/__main__.py +75 -0
- scitex/db/_check_health.py +381 -0
- scitex/db/_delete_duplicates.py +25 -386
- scitex/db/_inspect.py +335 -114
- scitex/db/_inspect_optimized.py +301 -0
- scitex/db/{_PostgreSQL.py → _postgresql/_PostgreSQL.py} +3 -3
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BackupMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BatchMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BlobMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_ConnectionMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_MaintenanceMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_QueryMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_SchemaMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_TransactionMixin.py +1 -1
- scitex/db/_postgresql/__init__.py +6 -0
- scitex/db/_sqlite3/_SQLite3.py +210 -0
- scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin.py +581 -0
- scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin_v01-need-_hash-col.py +517 -0
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_BatchMixin.py +1 -1
- scitex/db/_sqlite3/_SQLite3Mixins/_BlobMixin.py +281 -0
- scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin.py +548 -0
- scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin_v01-indentation-issues.py +583 -0
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_ConnectionMixin.py +29 -13
- scitex/db/_sqlite3/_SQLite3Mixins/_GitMixin.py +583 -0
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_ImportExportMixin.py +1 -1
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_IndexMixin.py +1 -1
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_MaintenanceMixin.py +2 -1
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_QueryMixin.py +37 -10
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_RowMixin.py +46 -6
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_TableMixin.py +56 -10
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_TransactionMixin.py +1 -1
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/__init__.py +14 -2
- scitex/db/_sqlite3/__init__.py +7 -0
- scitex/db/_sqlite3/_delete_duplicates.py +274 -0
- scitex/decorators/__init__.py +2 -0
- scitex/decorators/_cache_disk.py +13 -5
- scitex/decorators/_cache_disk_async.py +49 -0
- scitex/decorators/_deprecated.py +175 -10
- scitex/decorators/_timeout.py +1 -1
- scitex/dev/_analyze_code_flow.py +2 -2
- scitex/dict/_DotDict.py +73 -15
- scitex/dict/_DotDict_v01-not-handling-recursive-instantiations.py +442 -0
- scitex/dict/_DotDict_v02-not-serializing-Path-object.py +446 -0
- scitex/dict/__init__.py +2 -0
- scitex/dict/_flatten.py +27 -0
- scitex/dsp/_crop.py +2 -2
- scitex/dsp/_demo_sig.py +2 -2
- scitex/dsp/_detect_ripples.py +2 -2
- scitex/dsp/_hilbert.py +2 -2
- scitex/dsp/_listen.py +6 -6
- scitex/dsp/_modulation_index.py +2 -2
- scitex/dsp/_pac.py +1 -1
- scitex/dsp/_psd.py +2 -2
- scitex/dsp/_resample.py +2 -1
- scitex/dsp/_time.py +3 -2
- scitex/dsp/_wavelet.py +3 -2
- scitex/dsp/add_noise.py +2 -2
- scitex/dsp/example.py +1 -0
- scitex/dsp/filt.py +10 -9
- scitex/dsp/template.py +3 -2
- scitex/dsp/utils/_differential_bandpass_filters.py +1 -1
- scitex/dsp/utils/pac.py +2 -2
- scitex/dt/_normalize_timestamp.py +432 -0
- scitex/errors.py +572 -0
- scitex/gen/_DimHandler.py +2 -2
- scitex/gen/__init__.py +37 -7
- scitex/gen/_deprecated_close.py +80 -0
- scitex/gen/_deprecated_start.py +26 -0
- scitex/gen/_detect_environment.py +152 -0
- scitex/gen/_detect_notebook_path.py +169 -0
- scitex/gen/_embed.py +6 -2
- scitex/gen/_get_notebook_path.py +257 -0
- scitex/gen/_less.py +1 -1
- scitex/gen/_list_packages.py +2 -2
- scitex/gen/_norm.py +44 -9
- scitex/gen/_norm_cache.py +269 -0
- scitex/gen/_src.py +3 -5
- scitex/gen/_title_case.py +3 -3
- scitex/io/__init__.py +28 -6
- scitex/io/_glob.py +13 -7
- scitex/io/_load.py +108 -21
- scitex/io/_load_cache.py +303 -0
- scitex/io/_load_configs.py +40 -15
- scitex/io/{_H5Explorer.py → _load_modules/_H5Explorer.py} +80 -17
- scitex/io/_load_modules/_ZarrExplorer.py +114 -0
- scitex/io/_load_modules/_bibtex.py +207 -0
- scitex/io/_load_modules/_hdf5.py +53 -178
- scitex/io/_load_modules/_json.py +5 -3
- scitex/io/_load_modules/_pdf.py +871 -16
- scitex/io/_load_modules/_sqlite3.py +15 -0
- scitex/io/_load_modules/_txt.py +41 -12
- scitex/io/_load_modules/_yaml.py +4 -3
- scitex/io/_load_modules/_zarr.py +126 -0
- scitex/io/_save.py +429 -171
- scitex/io/_save_modules/__init__.py +6 -0
- scitex/io/_save_modules/_bibtex.py +194 -0
- scitex/io/_save_modules/_csv.py +8 -4
- scitex/io/_save_modules/_excel.py +174 -15
- scitex/io/_save_modules/_hdf5.py +251 -226
- scitex/io/_save_modules/_image.py +1 -3
- scitex/io/_save_modules/_json.py +49 -4
- scitex/io/_save_modules/_listed_dfs_as_csv.py +1 -3
- scitex/io/_save_modules/_listed_scalars_as_csv.py +1 -3
- scitex/io/_save_modules/_tex.py +277 -0
- scitex/io/_save_modules/_yaml.py +42 -3
- scitex/io/_save_modules/_zarr.py +160 -0
- scitex/io/utils/__init__.py +20 -0
- scitex/io/utils/h5_to_zarr.py +616 -0
- scitex/linalg/_geometric_median.py +6 -2
- scitex/{gen/_tee.py → logging/_Tee.py} +43 -84
- scitex/logging/__init__.py +122 -0
- scitex/logging/_config.py +158 -0
- scitex/logging/_context.py +103 -0
- scitex/logging/_formatters.py +128 -0
- scitex/logging/_handlers.py +64 -0
- scitex/logging/_levels.py +35 -0
- scitex/logging/_logger.py +163 -0
- scitex/logging/_print_capture.py +95 -0
- scitex/ml/__init__.py +69 -0
- scitex/{ai/genai/anthropic.py → ml/_gen_ai/_Anthropic.py} +13 -19
- scitex/{ai/genai/base_genai.py → ml/_gen_ai/_BaseGenAI.py} +5 -5
- scitex/{ai/genai/deepseek.py → ml/_gen_ai/_DeepSeek.py} +11 -16
- scitex/{ai/genai/google.py → ml/_gen_ai/_Google.py} +7 -15
- scitex/{ai/genai/groq.py → ml/_gen_ai/_Groq.py} +1 -8
- scitex/{ai/genai/llama.py → ml/_gen_ai/_Llama.py} +3 -16
- scitex/{ai/genai/openai.py → ml/_gen_ai/_OpenAI.py} +3 -3
- scitex/{ai/genai/params.py → ml/_gen_ai/_PARAMS.py} +51 -65
- scitex/{ai/genai/perplexity.py → ml/_gen_ai/_Perplexity.py} +3 -14
- scitex/ml/_gen_ai/__init__.py +43 -0
- scitex/{ai/genai/calc_cost.py → ml/_gen_ai/_calc_cost.py} +1 -1
- scitex/{ai/genai/format_output_func.py → ml/_gen_ai/_format_output_func.py} +4 -4
- scitex/{ai/genai/genai_factory.py → ml/_gen_ai/_genai_factory.py} +8 -8
- scitex/ml/activation/__init__.py +8 -0
- scitex/ml/activation/_define.py +11 -0
- scitex/{ai/classifier_server.py → ml/classification/Classifier.py} +5 -5
- scitex/ml/classification/CrossValidationExperiment.py +374 -0
- scitex/ml/classification/__init__.py +46 -0
- scitex/ml/classification/reporters/_BaseClassificationReporter.py +281 -0
- scitex/ml/classification/reporters/_ClassificationReporter.py +773 -0
- scitex/ml/classification/reporters/_MultiClassificationReporter.py +406 -0
- scitex/ml/classification/reporters/_SingleClassificationReporter.py +1834 -0
- scitex/ml/classification/reporters/__init__.py +11 -0
- scitex/ml/classification/reporters/reporter_utils/_Plotter.py +1028 -0
- scitex/ml/classification/reporters/reporter_utils/__init__.py +80 -0
- scitex/ml/classification/reporters/reporter_utils/aggregation.py +457 -0
- scitex/ml/classification/reporters/reporter_utils/data_models.py +313 -0
- scitex/ml/classification/reporters/reporter_utils/reporting.py +1056 -0
- scitex/ml/classification/reporters/reporter_utils/storage.py +221 -0
- scitex/ml/classification/reporters/reporter_utils/validation.py +395 -0
- scitex/ml/classification/timeseries/_TimeSeriesBlockingSplit.py +568 -0
- scitex/ml/classification/timeseries/_TimeSeriesCalendarSplit.py +688 -0
- scitex/ml/classification/timeseries/_TimeSeriesMetadata.py +139 -0
- scitex/ml/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +1716 -0
- scitex/ml/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +1685 -0
- scitex/ml/classification/timeseries/_TimeSeriesStrategy.py +84 -0
- scitex/ml/classification/timeseries/_TimeSeriesStratifiedSplit.py +610 -0
- scitex/ml/classification/timeseries/__init__.py +39 -0
- scitex/ml/classification/timeseries/_normalize_timestamp.py +436 -0
- scitex/ml/clustering/__init__.py +11 -0
- scitex/ml/clustering/_pca.py +115 -0
- scitex/ml/clustering/_umap.py +376 -0
- scitex/ml/feature_extraction/__init__.py +56 -0
- scitex/ml/feature_extraction/vit.py +149 -0
- scitex/ml/feature_selection/__init__.py +30 -0
- scitex/ml/feature_selection/feature_selection.py +364 -0
- scitex/ml/loss/_L1L2Losses.py +34 -0
- scitex/ml/loss/__init__.py +12 -0
- scitex/ml/loss/multi_task_loss.py +47 -0
- scitex/ml/metrics/__init__.py +56 -0
- scitex/ml/metrics/_calc_bacc.py +61 -0
- scitex/ml/metrics/_calc_bacc_from_conf_mat.py +38 -0
- scitex/ml/metrics/_calc_clf_report.py +78 -0
- scitex/ml/metrics/_calc_conf_mat.py +93 -0
- scitex/ml/metrics/_calc_feature_importance.py +183 -0
- scitex/ml/metrics/_calc_mcc.py +61 -0
- scitex/ml/metrics/_calc_pre_rec_auc.py +116 -0
- scitex/ml/metrics/_calc_roc_auc.py +110 -0
- scitex/ml/metrics/_calc_seizure_prediction_metrics.py +490 -0
- scitex/ml/metrics/_calc_silhouette_score.py +503 -0
- scitex/ml/metrics/_normalize_labels.py +83 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/__init__.py +0 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/__init__.py +3 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger.py +207 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger2020.py +238 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger913A.py +215 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/rangerqh.py +184 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/setup.py +24 -0
- scitex/ml/optim/__init__.py +13 -0
- scitex/ml/optim/_get_set.py +31 -0
- scitex/ml/optim/_optimizers.py +71 -0
- scitex/ml/plt/__init__.py +60 -0
- scitex/ml/plt/_plot_conf_mat.py +663 -0
- scitex/ml/plt/_plot_feature_importance.py +323 -0
- scitex/ml/plt/_plot_learning_curve.py +345 -0
- scitex/ml/plt/_plot_optuna_study.py +225 -0
- scitex/ml/plt/_plot_pre_rec_curve.py +290 -0
- scitex/ml/plt/_plot_roc_curve.py +255 -0
- scitex/ml/sk/__init__.py +11 -0
- scitex/ml/sk/_clf.py +58 -0
- scitex/ml/sk/_to_sktime.py +100 -0
- scitex/ml/sklearn/__init__.py +26 -0
- scitex/ml/sklearn/clf.py +58 -0
- scitex/ml/sklearn/to_sktime.py +100 -0
- scitex/{ai/training/early_stopping.py → ml/training/_EarlyStopping.py} +1 -2
- scitex/{ai → ml/training}/_LearningCurveLogger.py +198 -242
- scitex/ml/training/__init__.py +7 -0
- scitex/ml/utils/__init__.py +22 -0
- scitex/ml/utils/_check_params.py +50 -0
- scitex/ml/utils/_default_dataset.py +46 -0
- scitex/ml/utils/_format_samples_for_sktime.py +26 -0
- scitex/ml/utils/_label_encoder.py +134 -0
- scitex/ml/utils/_merge_labels.py +22 -0
- scitex/ml/utils/_sliding_window_data_augmentation.py +11 -0
- scitex/ml/utils/_under_sample.py +51 -0
- scitex/ml/utils/_verify_n_gpus.py +16 -0
- scitex/ml/utils/grid_search.py +148 -0
- scitex/nn/_BNet.py +15 -9
- scitex/nn/_Filters.py +2 -2
- scitex/nn/_ModulationIndex.py +2 -2
- scitex/nn/_PAC.py +1 -1
- scitex/nn/_Spectrogram.py +12 -3
- scitex/nn/__init__.py +9 -10
- scitex/path/__init__.py +18 -0
- scitex/path/_clean.py +4 -0
- scitex/path/_find.py +9 -4
- scitex/path/_symlink.py +348 -0
- scitex/path/_version.py +4 -3
- scitex/pd/__init__.py +2 -0
- scitex/pd/_get_unique.py +99 -0
- scitex/plt/__init__.py +114 -5
- scitex/plt/_subplots/_AxesWrapper.py +1 -3
- scitex/plt/_subplots/_AxisWrapper.py +7 -3
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +47 -13
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +160 -2
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +26 -4
- scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +322 -0
- scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +1 -0
- scitex/plt/_subplots/_FigWrapper.py +62 -6
- scitex/plt/_subplots/_export_as_csv.py +43 -27
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +5 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +81 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_bar.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_barh.py +20 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_boxplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_contour.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_errorbar.py +35 -18
- scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_fill.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_fill_between.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_hist.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +15 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_box.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_conf_mat.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_ecdf.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_fillv.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_heatmap.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_image.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_joyplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_line.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_mean_ci.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_mean_std.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_median_iqr.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_raster.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_rectangle.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +35 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter_hist.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_shaded_line.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_violin.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_scatter.py +6 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_barplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_boxplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_heatmap.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_histplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_kdeplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_scatterplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_stripplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_swarmplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_violinplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_text.py +60 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_violin.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_violinplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters.py +56 -59
- scitex/plt/ax/_style/_hide_spines.py +1 -3
- scitex/plt/ax/_style/_rotate_labels.py +180 -76
- scitex/plt/ax/_style/_rotate_labels_v01.py +248 -0
- scitex/plt/ax/_style/_set_meta.py +11 -4
- scitex/plt/ax/_style/_set_supxyt.py +3 -3
- scitex/plt/ax/_style/_set_xyt.py +3 -3
- scitex/plt/ax/_style/_share_axes.py +2 -2
- scitex/plt/color/__init__.py +4 -4
- scitex/plt/color/{_get_colors_from_cmap.py → _get_colors_from_conf_matap.py} +7 -7
- scitex/plt/utils/_configure_mpl.py +99 -86
- scitex/plt/utils/_histogram_utils.py +1 -3
- scitex/plt/utils/_is_valid_axis.py +1 -3
- scitex/plt/utils/_scitex_config.py +1 -0
- scitex/repro/__init__.py +75 -0
- scitex/{reproduce → repro}/_gen_ID.py +1 -1
- scitex/{reproduce → repro}/_gen_timestamp.py +1 -1
- scitex/repro_rng/_RandomStateManager.py +590 -0
- scitex/repro_rng/_RandomStateManager_v01-no-verbose-options.py +414 -0
- scitex/repro_rng/__init__.py +39 -0
- scitex/reproduce/__init__.py +25 -13
- scitex/reproduce/_hash_array.py +22 -0
- scitex/resource/_get_processor_usages.py +4 -4
- scitex/resource/_get_specs.py +2 -2
- scitex/resource/_log_processor_usages.py +2 -2
- scitex/rng/_RandomStateManager.py +590 -0
- scitex/rng/_RandomStateManager_v01-no-verbose-options.py +414 -0
- scitex/rng/__init__.py +39 -0
- scitex/scholar/__init__.py +309 -19
- scitex/scholar/__main__.py +319 -0
- scitex/scholar/auth/ScholarAuthManager.py +308 -0
- scitex/scholar/auth/__init__.py +12 -0
- scitex/scholar/auth/core/AuthenticationGateway.py +473 -0
- scitex/scholar/auth/core/BrowserAuthenticator.py +386 -0
- scitex/scholar/auth/core/StrategyResolver.py +309 -0
- scitex/scholar/auth/core/__init__.py +16 -0
- scitex/scholar/auth/gateway/_OpenURLLinkFinder.py +120 -0
- scitex/scholar/auth/gateway/_OpenURLResolver.py +209 -0
- scitex/scholar/auth/gateway/__init__.py +38 -0
- scitex/scholar/auth/gateway/_resolve_functions.py +101 -0
- scitex/scholar/auth/providers/BaseAuthenticator.py +166 -0
- scitex/scholar/auth/providers/EZProxyAuthenticator.py +484 -0
- scitex/scholar/auth/providers/OpenAthensAuthenticator.py +619 -0
- scitex/scholar/auth/providers/ShibbolethAuthenticator.py +686 -0
- scitex/scholar/auth/providers/__init__.py +18 -0
- scitex/scholar/auth/session/AuthCacheManager.py +189 -0
- scitex/scholar/auth/session/SessionManager.py +159 -0
- scitex/scholar/auth/session/__init__.py +11 -0
- scitex/scholar/auth/sso/BaseSSOAutomator.py +373 -0
- scitex/scholar/auth/sso/OpenAthensSSOAutomator.py +378 -0
- scitex/scholar/auth/sso/SSOAutomator.py +180 -0
- scitex/scholar/auth/sso/UniversityOfMelbourneSSOAutomator.py +380 -0
- scitex/scholar/auth/sso/__init__.py +15 -0
- scitex/scholar/browser/ScholarBrowserManager.py +705 -0
- scitex/scholar/browser/__init__.py +38 -0
- scitex/scholar/browser/utils/__init__.py +13 -0
- scitex/scholar/browser/utils/click_and_wait.py +205 -0
- scitex/scholar/browser/utils/close_unwanted_pages.py +140 -0
- scitex/scholar/browser/utils/wait_redirects.py +732 -0
- scitex/scholar/config/PublisherRules.py +132 -0
- scitex/scholar/config/ScholarConfig.py +126 -0
- scitex/scholar/config/__init__.py +17 -0
- scitex/scholar/core/Paper.py +627 -0
- scitex/scholar/core/Papers.py +722 -0
- scitex/scholar/core/Scholar.py +1975 -0
- scitex/scholar/core/__init__.py +9 -0
- scitex/scholar/impact_factor/ImpactFactorEngine.py +204 -0
- scitex/scholar/impact_factor/__init__.py +20 -0
- scitex/scholar/impact_factor/estimation/ImpactFactorEstimationEngine.py +0 -0
- scitex/scholar/impact_factor/estimation/__init__.py +40 -0
- scitex/scholar/impact_factor/estimation/build_database.py +0 -0
- scitex/scholar/impact_factor/estimation/core/__init__.py +28 -0
- scitex/scholar/impact_factor/estimation/core/cache_manager.py +523 -0
- scitex/scholar/impact_factor/estimation/core/calculator.py +355 -0
- scitex/scholar/impact_factor/estimation/core/journal_matcher.py +428 -0
- scitex/scholar/integration/__init__.py +59 -0
- scitex/scholar/integration/base.py +502 -0
- scitex/scholar/integration/mendeley/__init__.py +22 -0
- scitex/scholar/integration/mendeley/exporter.py +166 -0
- scitex/scholar/integration/mendeley/importer.py +236 -0
- scitex/scholar/integration/mendeley/linker.py +79 -0
- scitex/scholar/integration/mendeley/mapper.py +212 -0
- scitex/scholar/integration/zotero/__init__.py +27 -0
- scitex/scholar/integration/zotero/__main__.py +264 -0
- scitex/scholar/integration/zotero/exporter.py +351 -0
- scitex/scholar/integration/zotero/importer.py +372 -0
- scitex/scholar/integration/zotero/linker.py +415 -0
- scitex/scholar/integration/zotero/mapper.py +286 -0
- scitex/scholar/metadata_engines/ScholarEngine.py +588 -0
- scitex/scholar/metadata_engines/__init__.py +21 -0
- scitex/scholar/metadata_engines/individual/ArXivEngine.py +397 -0
- scitex/scholar/metadata_engines/individual/CrossRefEngine.py +274 -0
- scitex/scholar/metadata_engines/individual/CrossRefLocalEngine.py +263 -0
- scitex/scholar/metadata_engines/individual/OpenAlexEngine.py +350 -0
- scitex/scholar/metadata_engines/individual/PubMedEngine.py +329 -0
- scitex/scholar/metadata_engines/individual/SemanticScholarEngine.py +438 -0
- scitex/scholar/metadata_engines/individual/URLDOIEngine.py +410 -0
- scitex/scholar/metadata_engines/individual/_BaseDOIEngine.py +487 -0
- scitex/scholar/metadata_engines/individual/__init__.py +7 -0
- scitex/scholar/metadata_engines/utils/_PubMedConverter.py +469 -0
- scitex/scholar/metadata_engines/utils/_URLDOIExtractor.py +283 -0
- scitex/scholar/metadata_engines/utils/__init__.py +30 -0
- scitex/scholar/metadata_engines/utils/_metadata2bibtex.py +103 -0
- scitex/scholar/metadata_engines/utils/_standardize_metadata.py +376 -0
- scitex/scholar/pdf_download/ScholarPDFDownloader.py +579 -0
- scitex/scholar/pdf_download/__init__.py +5 -0
- scitex/scholar/pdf_download/strategies/__init__.py +38 -0
- scitex/scholar/pdf_download/strategies/chrome_pdf_viewer.py +376 -0
- scitex/scholar/pdf_download/strategies/direct_download.py +131 -0
- scitex/scholar/pdf_download/strategies/manual_download_fallback.py +167 -0
- scitex/scholar/pdf_download/strategies/manual_download_utils.py +996 -0
- scitex/scholar/pdf_download/strategies/response_body.py +207 -0
- scitex/scholar/pipelines/ScholarPipelineBibTeX.py +364 -0
- scitex/scholar/pipelines/ScholarPipelineParallel.py +478 -0
- scitex/scholar/pipelines/ScholarPipelineSingle.py +767 -0
- scitex/scholar/pipelines/__init__.py +49 -0
- scitex/scholar/storage/BibTeXHandler.py +1018 -0
- scitex/scholar/storage/PaperIO.py +468 -0
- scitex/scholar/storage/ScholarLibrary.py +182 -0
- scitex/scholar/storage/_DeduplicationManager.py +548 -0
- scitex/scholar/storage/_LibraryCacheManager.py +724 -0
- scitex/scholar/storage/_LibraryManager.py +1835 -0
- scitex/scholar/storage/__init__.py +28 -0
- scitex/scholar/url_finder/ScholarURLFinder.py +379 -0
- scitex/scholar/url_finder/__init__.py +7 -0
- scitex/scholar/url_finder/strategies/__init__.py +33 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_direct_links.py +261 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_dropdown.py +67 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_href.py +204 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_navigation.py +256 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_publisher_patterns.py +165 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_zotero_translators.py +163 -0
- scitex/scholar/url_finder/strategies/find_supplementary_urls_by_href.py +70 -0
- scitex/scholar/utils/__init__.py +22 -0
- scitex/scholar/utils/bibtex/__init__.py +9 -0
- scitex/scholar/utils/bibtex/_parse_bibtex.py +71 -0
- scitex/scholar/utils/cleanup/__init__.py +8 -0
- scitex/scholar/utils/cleanup/_cleanup_scholar_processes.py +96 -0
- scitex/scholar/utils/cleanup/cleanup_old_extractions.py +117 -0
- scitex/scholar/utils/text/_TextNormalizer.py +407 -0
- scitex/scholar/utils/text/__init__.py +9 -0
- scitex/scholar/zotero/__init__.py +38 -0
- scitex/session/__init__.py +51 -0
- scitex/session/_lifecycle.py +736 -0
- scitex/session/_manager.py +102 -0
- scitex/session/template.py +122 -0
- scitex/stats/__init__.py +30 -26
- scitex/stats/correct/__init__.py +21 -0
- scitex/stats/correct/_correct_bonferroni.py +551 -0
- scitex/stats/correct/_correct_fdr.py +634 -0
- scitex/stats/correct/_correct_holm.py +548 -0
- scitex/stats/correct/_correct_sidak.py +499 -0
- scitex/stats/descriptive/__init__.py +85 -0
- scitex/stats/descriptive/_circular.py +540 -0
- scitex/stats/descriptive/_describe.py +219 -0
- scitex/stats/descriptive/_nan.py +518 -0
- scitex/stats/descriptive/_real.py +189 -0
- scitex/stats/effect_sizes/__init__.py +41 -0
- scitex/stats/effect_sizes/_cliffs_delta.py +325 -0
- scitex/stats/effect_sizes/_cohens_d.py +342 -0
- scitex/stats/effect_sizes/_epsilon_squared.py +315 -0
- scitex/stats/effect_sizes/_eta_squared.py +302 -0
- scitex/stats/effect_sizes/_prob_superiority.py +296 -0
- scitex/stats/posthoc/__init__.py +19 -0
- scitex/stats/posthoc/_dunnett.py +463 -0
- scitex/stats/posthoc/_games_howell.py +383 -0
- scitex/stats/posthoc/_tukey_hsd.py +367 -0
- scitex/stats/power/__init__.py +19 -0
- scitex/stats/power/_power.py +433 -0
- scitex/stats/template.py +119 -0
- scitex/stats/utils/__init__.py +62 -0
- scitex/stats/utils/_effect_size.py +985 -0
- scitex/stats/utils/_formatters.py +270 -0
- scitex/stats/utils/_normalizers.py +927 -0
- scitex/stats/utils/_power.py +433 -0
- scitex/stats_v01/_EffectSizeCalculator.py +488 -0
- scitex/stats_v01/_StatisticalValidator.py +411 -0
- scitex/stats_v01/__init__.py +60 -0
- scitex/stats_v01/_additional_tests.py +415 -0
- scitex/{stats → stats_v01}/_p2stars.py +19 -5
- scitex/stats_v01/_two_sample_tests.py +141 -0
- scitex/stats_v01/desc/__init__.py +83 -0
- scitex/stats_v01/desc/_circular.py +540 -0
- scitex/stats_v01/desc/_describe.py +219 -0
- scitex/stats_v01/desc/_nan.py +518 -0
- scitex/{stats/desc/_nan.py → stats_v01/desc/_nan_v01-20250920_145731.py} +23 -12
- scitex/stats_v01/desc/_real.py +189 -0
- scitex/stats_v01/tests/__corr_test_optimized.py +221 -0
- scitex/stats_v01/tests/_corr_test_optimized.py +179 -0
- scitex/str/__init__.py +1 -3
- scitex/str/_clean_path.py +6 -2
- scitex/str/_latex_fallback.py +267 -160
- scitex/str/_parse.py +44 -36
- scitex/str/_printc.py +1 -3
- scitex/template/__init__.py +87 -0
- scitex/template/_create_project.py +267 -0
- scitex/template/create_pip_project.py +80 -0
- scitex/template/create_research.py +80 -0
- scitex/template/create_singularity.py +80 -0
- scitex/units.py +291 -0
- scitex/utils/_compress_hdf5.py +14 -3
- scitex/utils/_email.py +21 -2
- scitex/utils/_grid.py +6 -4
- scitex/utils/_notify.py +13 -10
- scitex/utils/_verify_scitex_format.py +589 -0
- scitex/utils/_verify_scitex_format_v01.py +370 -0
- scitex/utils/template.py +122 -0
- scitex/web/_search_pubmed.py +62 -16
- scitex-2.1.0.dist-info/LICENSE +21 -0
- scitex-2.1.0.dist-info/METADATA +677 -0
- scitex-2.1.0.dist-info/RECORD +919 -0
- {scitex-2.0.0.dist-info → scitex-2.1.0.dist-info}/WHEEL +1 -1
- scitex-2.1.0.dist-info/entry_points.txt +3 -0
- scitex/ai/__Classifiers.py +0 -101
- scitex/ai/classification/classification_reporter.py +0 -1137
- scitex/ai/classification/classifiers.py +0 -101
- scitex/ai/classification_reporter.py +0 -1161
- scitex/ai/genai/__init__.py +0 -277
- scitex/ai/genai/anthropic_provider.py +0 -320
- scitex/ai/genai/anthropic_refactored.py +0 -109
- scitex/ai/genai/auth_manager.py +0 -200
- scitex/ai/genai/base_provider.py +0 -291
- scitex/ai/genai/chat_history.py +0 -307
- scitex/ai/genai/cost_tracker.py +0 -276
- scitex/ai/genai/deepseek_provider.py +0 -251
- scitex/ai/genai/google_provider.py +0 -228
- scitex/ai/genai/groq_provider.py +0 -248
- scitex/ai/genai/image_processor.py +0 -250
- scitex/ai/genai/llama_provider.py +0 -214
- scitex/ai/genai/mock_provider.py +0 -127
- scitex/ai/genai/model_registry.py +0 -304
- scitex/ai/genai/openai_provider.py +0 -293
- scitex/ai/genai/perplexity_provider.py +0 -205
- scitex/ai/genai/provider_base.py +0 -302
- scitex/ai/genai/provider_factory.py +0 -370
- scitex/ai/genai/response_handler.py +0 -235
- scitex/ai/layer/_Pass.py +0 -21
- scitex/ai/layer/__init__.py +0 -10
- scitex/ai/layer/_switch.py +0 -8
- scitex/ai/metrics/_bACC.py +0 -51
- scitex/ai/plt/_learning_curve.py +0 -194
- scitex/ai/plt/_optuna_study.py +0 -111
- scitex/ai/plt/aucs/__init__.py +0 -2
- scitex/ai/plt/aucs/example.py +0 -60
- scitex/ai/plt/aucs/pre_rec_auc.py +0 -223
- scitex/ai/plt/aucs/roc_auc.py +0 -246
- scitex/ai/sampling/undersample.py +0 -29
- scitex/db/_SQLite3.py +0 -2136
- scitex/db/_SQLite3Mixins/_BlobMixin.py +0 -229
- scitex/gen/_close.py +0 -222
- scitex/gen/_start.py +0 -451
- scitex/general/__init__.py +0 -5
- scitex/io/_load_modules/_db.py +0 -24
- scitex/life/__init__.py +0 -10
- scitex/life/_monitor_rain.py +0 -49
- scitex/reproduce/_fix_seeds.py +0 -45
- scitex/res/__init__.py +0 -5
- scitex/scholar/_local_search.py +0 -454
- scitex/scholar/_paper.py +0 -244
- scitex/scholar/_pdf_downloader.py +0 -325
- scitex/scholar/_search.py +0 -393
- scitex/scholar/_vector_search.py +0 -370
- scitex/scholar/_web_sources.py +0 -457
- scitex/stats/desc/__init__.py +0 -40
- scitex-2.0.0.dist-info/METADATA +0 -307
- scitex-2.0.0.dist-info/RECORD +0 -572
- scitex-2.0.0.dist-info/licenses/LICENSE +0 -7
- /scitex/ai/{act → activation}/__init__.py +0 -0
- /scitex/ai/{act → activation}/_define.py +0 -0
- /scitex/ai/{early_stopping.py → training/_EarlyStopping.py} +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_ImportExportMixin.py +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_IndexMixin.py +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_RowMixin.py +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_TableMixin.py +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/__init__.py +0 -0
- /scitex/{stats → stats_v01}/_calc_partial_corr.py +0 -0
- /scitex/{stats → stats_v01}/_corr_test_multi.py +0 -0
- /scitex/{stats → stats_v01}/_corr_test_wrapper.py +0 -0
- /scitex/{stats → stats_v01}/_describe_wrapper.py +0 -0
- /scitex/{stats → stats_v01}/_multiple_corrections.py +0 -0
- /scitex/{stats → stats_v01}/_nan_stats.py +0 -0
- /scitex/{stats → stats_v01}/_p2stars_wrapper.py +0 -0
- /scitex/{stats → stats_v01}/_statistical_tests.py +0 -0
- /scitex/{stats/desc/_describe.py → stats_v01/desc/_describe_v01-20250920_145731.py} +0 -0
- /scitex/{stats/desc/_real.py → stats_v01/desc/_real_v01-20250920_145731.py} +0 -0
- /scitex/{stats → stats_v01}/multiple/__init__.py +0 -0
- /scitex/{stats → stats_v01}/multiple/_bonferroni_correction.py +0 -0
- /scitex/{stats → stats_v01}/multiple/_fdr_correction.py +0 -0
- /scitex/{stats → stats_v01}/multiple/_multicompair.py +0 -0
- /scitex/{stats → stats_v01}/tests/__corr_test.py +0 -0
- /scitex/{stats → stats_v01}/tests/__corr_test_multi.py +0 -0
- /scitex/{stats → stats_v01}/tests/__corr_test_single.py +0 -0
- /scitex/{stats → stats_v01}/tests/__init__.py +0 -0
- /scitex/{stats → stats_v01}/tests/_brunner_munzel_test.py +0 -0
- /scitex/{stats → stats_v01}/tests/_nocorrelation_test.py +0 -0
- /scitex/{stats → stats_v01}/tests/_smirnov_grubbs.py +0 -0
- {scitex-2.0.0.dist-info → scitex-2.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-10 03:24:07 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/scholar/auth/AuthenticationGateway.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import os
|
|
8
|
+
__FILE__ = (
|
|
9
|
+
"./src/scitex/scholar/auth/core/AuthenticationGateway.py"
|
|
10
|
+
)
|
|
11
|
+
__DIR__ = os.path.dirname(__FILE__)
|
|
12
|
+
# ----------------------------------------
|
|
13
|
+
|
|
14
|
+
__FILE__ = __file__
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
Authentication Gateway Pattern for Scholar Module
|
|
18
|
+
|
|
19
|
+
Provides transparent authentication layer that:
|
|
20
|
+
- Determines if URL requires authentication (config-based)
|
|
21
|
+
- Prepares authenticated browser context before URL finding
|
|
22
|
+
- Visits authentication gateways (OpenURL) to establish sessions
|
|
23
|
+
- Caches authentication state to avoid redundant operations
|
|
24
|
+
|
|
25
|
+
This keeps URL finders and PDF downloaders free of authentication logic.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from dataclasses import dataclass, field
|
|
29
|
+
from typing import Dict, List, Optional
|
|
30
|
+
|
|
31
|
+
from playwright.async_api import BrowserContext, Page
|
|
32
|
+
|
|
33
|
+
from scitex import logging
|
|
34
|
+
from scitex.scholar.config import ScholarConfig
|
|
35
|
+
|
|
36
|
+
logger = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class URLContext:
|
|
41
|
+
"""
|
|
42
|
+
Context for URL operations with authentication information.
|
|
43
|
+
|
|
44
|
+
This dataclass carries all information needed for URL resolution
|
|
45
|
+
and PDF download, including authentication state.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
doi: str
|
|
49
|
+
title: Optional[str] = None
|
|
50
|
+
url: Optional[str] = None # Publisher landing page URL
|
|
51
|
+
pdf_urls: List[str] = field(default_factory=list)
|
|
52
|
+
requires_auth: Optional[bool] = None
|
|
53
|
+
auth_provider: Optional[str] = None # openathens, ezproxy, shibboleth
|
|
54
|
+
auth_gateway_url: Optional[str] = None # OpenURL for establishing session
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class AuthenticationGateway:
|
|
58
|
+
"""
|
|
59
|
+
Transparent authentication layer for Scholar operations.
|
|
60
|
+
|
|
61
|
+
Responsibilities:
|
|
62
|
+
- Determine if URL requires authentication (config-based, no hardcoding)
|
|
63
|
+
- Prepare authenticated browser context
|
|
64
|
+
- Visit authentication gateways (OpenURL) to establish publisher sessions
|
|
65
|
+
- Cache authentication state for performance
|
|
66
|
+
|
|
67
|
+
This gateway sits between Scholar and URL/Download operations,
|
|
68
|
+
preparing authentication transparently before content access.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def name(self):
|
|
73
|
+
return self.__class__.__name__
|
|
74
|
+
|
|
75
|
+
def __init__(
|
|
76
|
+
self,
|
|
77
|
+
auth_manager, # ScholarAuthManager
|
|
78
|
+
browser_manager, # ScholarBrowserManager
|
|
79
|
+
config: ScholarConfig = None,
|
|
80
|
+
):
|
|
81
|
+
"""
|
|
82
|
+
Initialize authentication gateway.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
auth_manager: ScholarAuthManager instance
|
|
86
|
+
browser_manager: ScholarBrowserManager instance
|
|
87
|
+
config: ScholarConfig instance
|
|
88
|
+
"""
|
|
89
|
+
self.auth_manager = auth_manager
|
|
90
|
+
self.browser_manager = browser_manager
|
|
91
|
+
self.config = config or ScholarConfig()
|
|
92
|
+
self._auth_cache: Dict[str, bool] = {} # Cache visited gateways
|
|
93
|
+
|
|
94
|
+
async def prepare_context_async(
|
|
95
|
+
self, doi: str, context: BrowserContext, title: Optional[str] = None
|
|
96
|
+
) -> URLContext:
|
|
97
|
+
"""
|
|
98
|
+
Prepare URL context with authentication if needed.
|
|
99
|
+
|
|
100
|
+
This is the main entry point - called BEFORE URL finding.
|
|
101
|
+
|
|
102
|
+
Flow:
|
|
103
|
+
1. Build OpenURL (authentication gateway)
|
|
104
|
+
2. Check if DOI needs authentication (based on known publishers)
|
|
105
|
+
3. If auth needed: Visit OpenURL to establish publisher cookies
|
|
106
|
+
4. Resolve to final publisher URL
|
|
107
|
+
5. Return prepared context with authenticated session
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
doi: Paper DOI
|
|
111
|
+
context: Browser context (will be updated with auth cookies)
|
|
112
|
+
title: Optional paper title
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
URLContext with authentication prepared and ready
|
|
116
|
+
"""
|
|
117
|
+
url_context = URLContext(doi=doi, title=title)
|
|
118
|
+
|
|
119
|
+
# Step 1: Build OpenURL
|
|
120
|
+
from scitex.scholar.auth.gateway._OpenURLResolver import (
|
|
121
|
+
OpenURLResolver,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
resolver = OpenURLResolver(config=self.config)
|
|
125
|
+
openurl = resolver._build_query(url_context.doi)
|
|
126
|
+
url_context.auth_gateway_url = openurl
|
|
127
|
+
|
|
128
|
+
# Step 2: Try to determine if auth needed from DOI patterns
|
|
129
|
+
# (IEEE DOIs start with 10.1109, Springer with 10.1007, etc.)
|
|
130
|
+
url_context = self._check_auth_requirements_from_doi(url_context)
|
|
131
|
+
|
|
132
|
+
# Step 3: If authentication needed, visit OpenURL and establish cookies
|
|
133
|
+
# This also resolves to the publisher URL as a side effect
|
|
134
|
+
if url_context.requires_auth:
|
|
135
|
+
publisher_url = await self._establish_authentication_async(
|
|
136
|
+
url_context, context
|
|
137
|
+
)
|
|
138
|
+
url_context.url = publisher_url or openurl
|
|
139
|
+
else:
|
|
140
|
+
# Step 4: For open access, use direct DOI navigation (faster than OpenURL)
|
|
141
|
+
from scitex.scholar.auth.gateway._resolve_functions import (
|
|
142
|
+
resolve_publisher_url_by_navigating_to_doi_page,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
page = await context.new_page()
|
|
146
|
+
try:
|
|
147
|
+
# Try direct DOI navigation first (fast for open access)
|
|
148
|
+
publisher_url = await resolve_publisher_url_by_navigating_to_doi_page(
|
|
149
|
+
url_context.doi, page
|
|
150
|
+
)
|
|
151
|
+
url_context.url = publisher_url
|
|
152
|
+
logger.debug(
|
|
153
|
+
f"{self.name}: Resolved {url_context.doi} → {publisher_url}"
|
|
154
|
+
)
|
|
155
|
+
except Exception as e:
|
|
156
|
+
# Fallback to OpenURL resolver if direct navigation fails
|
|
157
|
+
logger.debug(
|
|
158
|
+
f"{self.name}: Direct navigation failed, trying OpenURL: {e}"
|
|
159
|
+
)
|
|
160
|
+
try:
|
|
161
|
+
publisher_url = await resolver.resolve_doi(
|
|
162
|
+
url_context.doi, page
|
|
163
|
+
)
|
|
164
|
+
url_context.url = publisher_url
|
|
165
|
+
except Exception as openurl_error:
|
|
166
|
+
logger.warning(
|
|
167
|
+
f"{self.name}: Both methods failed for {url_context.doi}: {openurl_error}"
|
|
168
|
+
)
|
|
169
|
+
url_context.url = openurl # Last resort fallback
|
|
170
|
+
finally:
|
|
171
|
+
await page.close()
|
|
172
|
+
|
|
173
|
+
return url_context
|
|
174
|
+
|
|
175
|
+
async def _resolve_publisher_url_async(
|
|
176
|
+
self, url_context: URLContext, context: BrowserContext
|
|
177
|
+
) -> URLContext:
|
|
178
|
+
"""
|
|
179
|
+
Resolve DOI to publisher landing page URL.
|
|
180
|
+
|
|
181
|
+
Uses OpenURLResolver which already exists and works.
|
|
182
|
+
The OpenURL is the authentication gateway for paywalled content.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
url_context: URLContext with DOI
|
|
186
|
+
context: Browser context
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
URLContext with url and auth_gateway_url populated
|
|
190
|
+
"""
|
|
191
|
+
from scitex.scholar.auth.gateway._OpenURLResolver import (
|
|
192
|
+
OpenURLResolver,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
resolver = OpenURLResolver(config=self.config)
|
|
196
|
+
|
|
197
|
+
# Build OpenURL (this is the authentication gateway)
|
|
198
|
+
# Use the private _build_query method since no public method exists
|
|
199
|
+
openurl = resolver._build_query(url_context.doi)
|
|
200
|
+
url_context.auth_gateway_url = openurl
|
|
201
|
+
|
|
202
|
+
# Resolve to publisher URL (may redirect through OpenAthens)
|
|
203
|
+
page = await context.new_page()
|
|
204
|
+
try:
|
|
205
|
+
publisher_url = await resolver.resolve_doi(url_context.doi, page)
|
|
206
|
+
url_context.url = publisher_url
|
|
207
|
+
logger.debug(
|
|
208
|
+
f"{self.name}: Resolved {url_context.doi} → {publisher_url}"
|
|
209
|
+
)
|
|
210
|
+
except Exception as e:
|
|
211
|
+
logger.warning(
|
|
212
|
+
f"{self.name}: Failed to resolve DOI {url_context.doi}: {e}"
|
|
213
|
+
)
|
|
214
|
+
url_context.url = openurl # Fallback to OpenURL
|
|
215
|
+
finally:
|
|
216
|
+
await page.close()
|
|
217
|
+
|
|
218
|
+
return url_context
|
|
219
|
+
|
|
220
|
+
def _check_auth_requirements_from_doi(
|
|
221
|
+
self, url_context: URLContext
|
|
222
|
+
) -> URLContext:
|
|
223
|
+
"""
|
|
224
|
+
Determine if DOI requires authentication based on DOI prefix patterns.
|
|
225
|
+
|
|
226
|
+
This allows early detection before resolving URL.
|
|
227
|
+
IEEE DOIs start with 10.1109, Springer with 10.1007, etc.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
url_context: URLContext with doi populated
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
URLContext with requires_auth and auth_provider populated
|
|
234
|
+
"""
|
|
235
|
+
# Get authenticated publishers from config
|
|
236
|
+
# auth_config = self.config.get("authentication") or {}
|
|
237
|
+
# paywalled_publishers = auth_config.get("paywalled_publishers") or []
|
|
238
|
+
paywalled_publishers = self.config.resolve(
|
|
239
|
+
"paywalled_publishers", None, default=[]
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
doi = url_context.doi or ""
|
|
243
|
+
|
|
244
|
+
for publisher_config in paywalled_publishers:
|
|
245
|
+
doi_prefixes = publisher_config.get("doi_prefixes", [])
|
|
246
|
+
for prefix in doi_prefixes:
|
|
247
|
+
if doi.startswith(prefix):
|
|
248
|
+
url_context.requires_auth = True
|
|
249
|
+
url_context.auth_provider = publisher_config.get(
|
|
250
|
+
"preferred_provider", "openathens"
|
|
251
|
+
)
|
|
252
|
+
logger.info(
|
|
253
|
+
f"{self.name}: Authentication required for {publisher_config.get('name')} "
|
|
254
|
+
f"(DOI prefix: {prefix}, provider: {url_context.auth_provider})"
|
|
255
|
+
)
|
|
256
|
+
return url_context
|
|
257
|
+
|
|
258
|
+
# Fallback: check by URL if DOI detection didn't match
|
|
259
|
+
# (for cases where DOI prefix is not in config)
|
|
260
|
+
url_context.requires_auth = False
|
|
261
|
+
return url_context
|
|
262
|
+
|
|
263
|
+
def _check_auth_requirements(self, url_context: URLContext) -> URLContext:
|
|
264
|
+
"""
|
|
265
|
+
Determine if URL requires authentication based on config.
|
|
266
|
+
|
|
267
|
+
This is config-based (no hardcoded domain lists).
|
|
268
|
+
Checks URL against paywalled_publishers in config.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
url_context: URLContext with url populated
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
URLContext with requires_auth and auth_provider populated
|
|
275
|
+
"""
|
|
276
|
+
# Get authenticated publishers from config
|
|
277
|
+
# auth_config = self.config.get("authentication") or {}
|
|
278
|
+
# paywalled_publishers = auth_config.get("paywalled_publishers") or []
|
|
279
|
+
paywalled_publishers = self.config.resolve(
|
|
280
|
+
"paywalled_publishers", None, default=[]
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# Check if URL matches any paywalled publisher
|
|
284
|
+
url_lower = (url_context.url or "").lower()
|
|
285
|
+
|
|
286
|
+
for publisher_config in paywalled_publishers:
|
|
287
|
+
domain_patterns = publisher_config.get("domain_patterns", [])
|
|
288
|
+
for pattern in domain_patterns:
|
|
289
|
+
if pattern.lower() in url_lower:
|
|
290
|
+
url_context.requires_auth = True
|
|
291
|
+
url_context.auth_provider = publisher_config.get(
|
|
292
|
+
"preferred_provider", "openathens"
|
|
293
|
+
)
|
|
294
|
+
logger.info(
|
|
295
|
+
f"{self.name}: Authentication required for {publisher_config.get('name')} "
|
|
296
|
+
f"(provider: {url_context.auth_provider})"
|
|
297
|
+
)
|
|
298
|
+
return url_context
|
|
299
|
+
|
|
300
|
+
# No authentication required
|
|
301
|
+
url_context.requires_auth = False
|
|
302
|
+
return url_context
|
|
303
|
+
|
|
304
|
+
async def _establish_authentication_async(
|
|
305
|
+
self, url_context: URLContext, context: BrowserContext
|
|
306
|
+
) -> Optional[str]:
|
|
307
|
+
"""
|
|
308
|
+
Establish authentication by visiting gateway URL and clicking through to publisher.
|
|
309
|
+
|
|
310
|
+
This is the KEY OPERATION that solves the IEEE issue:
|
|
311
|
+
1. Visit OpenURL (library resolver)
|
|
312
|
+
2. Find publisher link on resolver page
|
|
313
|
+
3. Click link → redirects through OpenAthens → lands at publisher
|
|
314
|
+
4. Publisher session cookies established in browser context
|
|
315
|
+
|
|
316
|
+
Without this step:
|
|
317
|
+
- OpenAthens cookies exist at openathens.net
|
|
318
|
+
- NO cookies exist at ieee.org
|
|
319
|
+
- Chrome PDF viewer opens but download fails
|
|
320
|
+
|
|
321
|
+
With this step:
|
|
322
|
+
- Visit OpenURL
|
|
323
|
+
- Click IEEE link → redirect through OpenAthens
|
|
324
|
+
- Land at ieee.org → IEEE session cookies established
|
|
325
|
+
- Now ieee.org has cookies, Chrome PDF viewer works
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
url_context: URLContext with auth_gateway_url and doi
|
|
329
|
+
context: Browser context (will receive publisher cookies)
|
|
330
|
+
|
|
331
|
+
Returns:
|
|
332
|
+
Publisher URL if successful, None otherwise
|
|
333
|
+
"""
|
|
334
|
+
gateway_url = url_context.auth_gateway_url
|
|
335
|
+
|
|
336
|
+
if not gateway_url:
|
|
337
|
+
logger.warning(
|
|
338
|
+
f"{self.name}: No gateway URL available for authentication"
|
|
339
|
+
)
|
|
340
|
+
return None
|
|
341
|
+
|
|
342
|
+
# Check cache - avoid redundant visits
|
|
343
|
+
cache_key = f"{url_context.doi}"
|
|
344
|
+
if cache_key in self._auth_cache:
|
|
345
|
+
logger.debug(
|
|
346
|
+
f"{self.name}: Authentication already established for {url_context.doi}"
|
|
347
|
+
)
|
|
348
|
+
# Return cached URL if available
|
|
349
|
+
return self._auth_cache.get(f"{cache_key}_url")
|
|
350
|
+
|
|
351
|
+
logger.info(
|
|
352
|
+
f"{self.name}: Establishing auth via OpenURL",
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
# Visit OpenURL and click through to publisher
|
|
356
|
+
# This uses the existing OpenURLResolver flow
|
|
357
|
+
from scitex.browser import browser_logger
|
|
358
|
+
from scitex.scholar.auth.gateway._OpenURLResolver import (
|
|
359
|
+
OpenURLResolver,
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
resolver = OpenURLResolver(config=self.config)
|
|
363
|
+
page = await context.new_page()
|
|
364
|
+
|
|
365
|
+
try:
|
|
366
|
+
publisher_url = await resolver.resolve_doi(url_context.doi, page)
|
|
367
|
+
|
|
368
|
+
if publisher_url:
|
|
369
|
+
logger.info(f"{self.name}: Auth established")
|
|
370
|
+
await browser_logger.info(
|
|
371
|
+
page,
|
|
372
|
+
f"{self.name}: ✓ Session established at {publisher_url[:60]}",
|
|
373
|
+
)
|
|
374
|
+
await page.wait_for_timeout(2000)
|
|
375
|
+
# Cache successful authentication
|
|
376
|
+
self._auth_cache[cache_key] = True
|
|
377
|
+
self._auth_cache[f"{cache_key}_url"] = publisher_url
|
|
378
|
+
return publisher_url
|
|
379
|
+
else:
|
|
380
|
+
logger.warning(f"{self.name}: OpenURL resolution failed")
|
|
381
|
+
await browser_logger.info(
|
|
382
|
+
page, f"{self.name}: ✗ Could not resolve to publisher URL"
|
|
383
|
+
)
|
|
384
|
+
await page.wait_for_timeout(2000)
|
|
385
|
+
return None
|
|
386
|
+
|
|
387
|
+
except Exception as e:
|
|
388
|
+
logger.warning(f"{self.name}: Auth setup failed: {e}")
|
|
389
|
+
try:
|
|
390
|
+
await browser_logger.info(
|
|
391
|
+
page, f"{self.name}: ✗ EXCEPTION: {str(e)[:80]}"
|
|
392
|
+
)
|
|
393
|
+
await page.wait_for_timeout(2000)
|
|
394
|
+
except:
|
|
395
|
+
pass
|
|
396
|
+
# Don't raise - allow downstream to try anyway
|
|
397
|
+
return None
|
|
398
|
+
finally:
|
|
399
|
+
await page.close()
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
async def main_async():
|
|
403
|
+
"""
|
|
404
|
+
Demonstration of AuthenticationGateway usage.
|
|
405
|
+
|
|
406
|
+
Shows how to:
|
|
407
|
+
1. Initialize authentication components
|
|
408
|
+
2. Prepare authenticated browser context
|
|
409
|
+
3. Use the context for subsequent operations
|
|
410
|
+
"""
|
|
411
|
+
from scitex.scholar.auth.ScholarAuthManager import ScholarAuthManager
|
|
412
|
+
from scitex.scholar.browser.ScholarBrowserManager import (
|
|
413
|
+
ScholarBrowserManager,
|
|
414
|
+
)
|
|
415
|
+
from scitex.scholar.config import ScholarConfig
|
|
416
|
+
|
|
417
|
+
# Initialize components
|
|
418
|
+
config = ScholarConfig()
|
|
419
|
+
auth_manager = ScholarAuthManager(config=config)
|
|
420
|
+
browser_manager = ScholarBrowserManager(
|
|
421
|
+
auth_manager=auth_manager, config=config
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# Initialize gateway
|
|
425
|
+
gateway = AuthenticationGateway(
|
|
426
|
+
auth_manager=auth_manager,
|
|
427
|
+
browser_manager=browser_manager,
|
|
428
|
+
config=config,
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
# Example DOIs - one paywalled (IEEE), one open access
|
|
432
|
+
test_dois = [
|
|
433
|
+
"10.1109/JBHI.2024.1234567", # IEEE (paywalled)
|
|
434
|
+
"10.1088/1741-2552/aaf92e", # IOP Publishing (paywalled)
|
|
435
|
+
"10.1038/s41467-020-12345-6", # Nature Communications (open access)
|
|
436
|
+
]
|
|
437
|
+
|
|
438
|
+
# Get authenticated browser context
|
|
439
|
+
browser, context = (
|
|
440
|
+
await browser_manager.get_authenticated_browser_and_context_async()
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
try:
|
|
444
|
+
for doi in test_dois:
|
|
445
|
+
logger.info(f"\n{'='*60}")
|
|
446
|
+
logger.info(f"Testing DOI: {doi}")
|
|
447
|
+
logger.info(f"{'='*60}")
|
|
448
|
+
|
|
449
|
+
# Prepare authentication (this is the key operation)
|
|
450
|
+
url_context = await gateway.prepare_context_async(
|
|
451
|
+
doi=doi, context=context
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
# Show results
|
|
455
|
+
logger.info(f"Publisher URL: {url_context.url}")
|
|
456
|
+
logger.info(f"Requires auth: {url_context.requires_auth}")
|
|
457
|
+
logger.info(f"Auth provider: {url_context.auth_provider}")
|
|
458
|
+
logger.info(f"Gateway URL: {url_context.auth_gateway_url}")
|
|
459
|
+
|
|
460
|
+
# At this point, the browser context has publisher cookies
|
|
461
|
+
# You can now use it for URL finding or PDF download
|
|
462
|
+
|
|
463
|
+
finally:
|
|
464
|
+
await context.close()
|
|
465
|
+
await browser.close()
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
if __name__ == "__main__":
|
|
469
|
+
import asyncio
|
|
470
|
+
|
|
471
|
+
asyncio.run(main_async())
|
|
472
|
+
|
|
473
|
+
# EOF
|