tensorbored 2.21.0rc1769983804__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.
Files changed (271) hide show
  1. tensorbored/__init__.py +112 -0
  2. tensorbored/_vendor/__init__.py +0 -0
  3. tensorbored/_vendor/bleach/__init__.py +125 -0
  4. tensorbored/_vendor/bleach/_vendor/__init__.py +0 -0
  5. tensorbored/_vendor/bleach/_vendor/html5lib/__init__.py +35 -0
  6. tensorbored/_vendor/bleach/_vendor/html5lib/_ihatexml.py +289 -0
  7. tensorbored/_vendor/bleach/_vendor/html5lib/_inputstream.py +918 -0
  8. tensorbored/_vendor/bleach/_vendor/html5lib/_tokenizer.py +1735 -0
  9. tensorbored/_vendor/bleach/_vendor/html5lib/_trie/__init__.py +5 -0
  10. tensorbored/_vendor/bleach/_vendor/html5lib/_trie/_base.py +40 -0
  11. tensorbored/_vendor/bleach/_vendor/html5lib/_trie/py.py +67 -0
  12. tensorbored/_vendor/bleach/_vendor/html5lib/_utils.py +159 -0
  13. tensorbored/_vendor/bleach/_vendor/html5lib/constants.py +2946 -0
  14. tensorbored/_vendor/bleach/_vendor/html5lib/filters/__init__.py +0 -0
  15. tensorbored/_vendor/bleach/_vendor/html5lib/filters/alphabeticalattributes.py +29 -0
  16. tensorbored/_vendor/bleach/_vendor/html5lib/filters/base.py +12 -0
  17. tensorbored/_vendor/bleach/_vendor/html5lib/filters/inject_meta_charset.py +73 -0
  18. tensorbored/_vendor/bleach/_vendor/html5lib/filters/lint.py +93 -0
  19. tensorbored/_vendor/bleach/_vendor/html5lib/filters/optionaltags.py +207 -0
  20. tensorbored/_vendor/bleach/_vendor/html5lib/filters/sanitizer.py +916 -0
  21. tensorbored/_vendor/bleach/_vendor/html5lib/filters/whitespace.py +38 -0
  22. tensorbored/_vendor/bleach/_vendor/html5lib/html5parser.py +2795 -0
  23. tensorbored/_vendor/bleach/_vendor/html5lib/serializer.py +409 -0
  24. tensorbored/_vendor/bleach/_vendor/html5lib/treeadapters/__init__.py +30 -0
  25. tensorbored/_vendor/bleach/_vendor/html5lib/treeadapters/genshi.py +54 -0
  26. tensorbored/_vendor/bleach/_vendor/html5lib/treeadapters/sax.py +50 -0
  27. tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/__init__.py +88 -0
  28. tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/base.py +417 -0
  29. tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/dom.py +239 -0
  30. tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/etree.py +343 -0
  31. tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/etree_lxml.py +392 -0
  32. tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/__init__.py +154 -0
  33. tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/base.py +252 -0
  34. tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/dom.py +43 -0
  35. tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/etree.py +131 -0
  36. tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/etree_lxml.py +215 -0
  37. tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/genshi.py +69 -0
  38. tensorbored/_vendor/bleach/_vendor/parse.py +1078 -0
  39. tensorbored/_vendor/bleach/callbacks.py +32 -0
  40. tensorbored/_vendor/bleach/html5lib_shim.py +757 -0
  41. tensorbored/_vendor/bleach/linkifier.py +633 -0
  42. tensorbored/_vendor/bleach/parse_shim.py +1 -0
  43. tensorbored/_vendor/bleach/sanitizer.py +638 -0
  44. tensorbored/_vendor/bleach/six_shim.py +19 -0
  45. tensorbored/_vendor/webencodings/__init__.py +342 -0
  46. tensorbored/_vendor/webencodings/labels.py +231 -0
  47. tensorbored/_vendor/webencodings/mklabels.py +59 -0
  48. tensorbored/_vendor/webencodings/x_user_defined.py +325 -0
  49. tensorbored/assets.py +36 -0
  50. tensorbored/auth.py +102 -0
  51. tensorbored/backend/__init__.py +0 -0
  52. tensorbored/backend/application.py +604 -0
  53. tensorbored/backend/auth_context_middleware.py +38 -0
  54. tensorbored/backend/client_feature_flags.py +113 -0
  55. tensorbored/backend/empty_path_redirect.py +46 -0
  56. tensorbored/backend/event_processing/__init__.py +0 -0
  57. tensorbored/backend/event_processing/data_ingester.py +276 -0
  58. tensorbored/backend/event_processing/data_provider.py +535 -0
  59. tensorbored/backend/event_processing/directory_loader.py +142 -0
  60. tensorbored/backend/event_processing/directory_watcher.py +272 -0
  61. tensorbored/backend/event_processing/event_accumulator.py +950 -0
  62. tensorbored/backend/event_processing/event_file_inspector.py +463 -0
  63. tensorbored/backend/event_processing/event_file_loader.py +292 -0
  64. tensorbored/backend/event_processing/event_multiplexer.py +521 -0
  65. tensorbored/backend/event_processing/event_util.py +68 -0
  66. tensorbored/backend/event_processing/io_wrapper.py +223 -0
  67. tensorbored/backend/event_processing/plugin_asset_util.py +104 -0
  68. tensorbored/backend/event_processing/plugin_event_accumulator.py +721 -0
  69. tensorbored/backend/event_processing/plugin_event_multiplexer.py +522 -0
  70. tensorbored/backend/event_processing/reservoir.py +266 -0
  71. tensorbored/backend/event_processing/tag_types.py +29 -0
  72. tensorbored/backend/experiment_id.py +71 -0
  73. tensorbored/backend/experimental_plugin.py +51 -0
  74. tensorbored/backend/http_util.py +263 -0
  75. tensorbored/backend/json_util.py +70 -0
  76. tensorbored/backend/path_prefix.py +67 -0
  77. tensorbored/backend/process_graph.py +74 -0
  78. tensorbored/backend/security_validator.py +202 -0
  79. tensorbored/compat/__init__.py +69 -0
  80. tensorbored/compat/proto/__init__.py +0 -0
  81. tensorbored/compat/proto/allocation_description_pb2.py +35 -0
  82. tensorbored/compat/proto/api_def_pb2.py +82 -0
  83. tensorbored/compat/proto/attr_value_pb2.py +80 -0
  84. tensorbored/compat/proto/cluster_pb2.py +58 -0
  85. tensorbored/compat/proto/config_pb2.py +271 -0
  86. tensorbored/compat/proto/coordination_config_pb2.py +45 -0
  87. tensorbored/compat/proto/cost_graph_pb2.py +87 -0
  88. tensorbored/compat/proto/cpp_shape_inference_pb2.py +70 -0
  89. tensorbored/compat/proto/debug_pb2.py +65 -0
  90. tensorbored/compat/proto/event_pb2.py +149 -0
  91. tensorbored/compat/proto/full_type_pb2.py +74 -0
  92. tensorbored/compat/proto/function_pb2.py +157 -0
  93. tensorbored/compat/proto/graph_debug_info_pb2.py +111 -0
  94. tensorbored/compat/proto/graph_pb2.py +41 -0
  95. tensorbored/compat/proto/histogram_pb2.py +39 -0
  96. tensorbored/compat/proto/meta_graph_pb2.py +254 -0
  97. tensorbored/compat/proto/node_def_pb2.py +61 -0
  98. tensorbored/compat/proto/op_def_pb2.py +81 -0
  99. tensorbored/compat/proto/resource_handle_pb2.py +48 -0
  100. tensorbored/compat/proto/rewriter_config_pb2.py +93 -0
  101. tensorbored/compat/proto/rpc_options_pb2.py +35 -0
  102. tensorbored/compat/proto/saved_object_graph_pb2.py +193 -0
  103. tensorbored/compat/proto/saver_pb2.py +38 -0
  104. tensorbored/compat/proto/step_stats_pb2.py +116 -0
  105. tensorbored/compat/proto/struct_pb2.py +144 -0
  106. tensorbored/compat/proto/summary_pb2.py +111 -0
  107. tensorbored/compat/proto/tensor_description_pb2.py +38 -0
  108. tensorbored/compat/proto/tensor_pb2.py +68 -0
  109. tensorbored/compat/proto/tensor_shape_pb2.py +46 -0
  110. tensorbored/compat/proto/tfprof_log_pb2.py +307 -0
  111. tensorbored/compat/proto/trackable_object_graph_pb2.py +90 -0
  112. tensorbored/compat/proto/types_pb2.py +105 -0
  113. tensorbored/compat/proto/variable_pb2.py +62 -0
  114. tensorbored/compat/proto/verifier_config_pb2.py +38 -0
  115. tensorbored/compat/proto/versions_pb2.py +35 -0
  116. tensorbored/compat/tensorflow_stub/__init__.py +38 -0
  117. tensorbored/compat/tensorflow_stub/app.py +124 -0
  118. tensorbored/compat/tensorflow_stub/compat/__init__.py +131 -0
  119. tensorbored/compat/tensorflow_stub/compat/v1/__init__.py +20 -0
  120. tensorbored/compat/tensorflow_stub/dtypes.py +692 -0
  121. tensorbored/compat/tensorflow_stub/error_codes.py +169 -0
  122. tensorbored/compat/tensorflow_stub/errors.py +507 -0
  123. tensorbored/compat/tensorflow_stub/flags.py +124 -0
  124. tensorbored/compat/tensorflow_stub/io/__init__.py +17 -0
  125. tensorbored/compat/tensorflow_stub/io/gfile.py +1011 -0
  126. tensorbored/compat/tensorflow_stub/pywrap_tensorflow.py +285 -0
  127. tensorbored/compat/tensorflow_stub/tensor_shape.py +1035 -0
  128. tensorbored/context.py +129 -0
  129. tensorbored/data/__init__.py +0 -0
  130. tensorbored/data/grpc_provider.py +365 -0
  131. tensorbored/data/ingester.py +46 -0
  132. tensorbored/data/proto/__init__.py +0 -0
  133. tensorbored/data/proto/data_provider_pb2.py +517 -0
  134. tensorbored/data/proto/data_provider_pb2_grpc.py +374 -0
  135. tensorbored/data/provider.py +1365 -0
  136. tensorbored/data/server_ingester.py +301 -0
  137. tensorbored/data_compat.py +159 -0
  138. tensorbored/dataclass_compat.py +224 -0
  139. tensorbored/default.py +124 -0
  140. tensorbored/errors.py +130 -0
  141. tensorbored/lazy.py +99 -0
  142. tensorbored/main.py +48 -0
  143. tensorbored/main_lib.py +62 -0
  144. tensorbored/manager.py +487 -0
  145. tensorbored/notebook.py +441 -0
  146. tensorbored/plugin_util.py +266 -0
  147. tensorbored/plugins/__init__.py +0 -0
  148. tensorbored/plugins/audio/__init__.py +0 -0
  149. tensorbored/plugins/audio/audio_plugin.py +229 -0
  150. tensorbored/plugins/audio/metadata.py +69 -0
  151. tensorbored/plugins/audio/plugin_data_pb2.py +37 -0
  152. tensorbored/plugins/audio/summary.py +230 -0
  153. tensorbored/plugins/audio/summary_v2.py +124 -0
  154. tensorbored/plugins/base_plugin.py +367 -0
  155. tensorbored/plugins/core/__init__.py +0 -0
  156. tensorbored/plugins/core/core_plugin.py +981 -0
  157. tensorbored/plugins/custom_scalar/__init__.py +0 -0
  158. tensorbored/plugins/custom_scalar/custom_scalars_plugin.py +320 -0
  159. tensorbored/plugins/custom_scalar/layout_pb2.py +85 -0
  160. tensorbored/plugins/custom_scalar/metadata.py +35 -0
  161. tensorbored/plugins/custom_scalar/summary.py +79 -0
  162. tensorbored/plugins/debugger_v2/__init__.py +0 -0
  163. tensorbored/plugins/debugger_v2/debug_data_multiplexer.py +631 -0
  164. tensorbored/plugins/debugger_v2/debug_data_provider.py +634 -0
  165. tensorbored/plugins/debugger_v2/debugger_v2_plugin.py +504 -0
  166. tensorbored/plugins/distribution/__init__.py +0 -0
  167. tensorbored/plugins/distribution/compressor.py +158 -0
  168. tensorbored/plugins/distribution/distributions_plugin.py +116 -0
  169. tensorbored/plugins/distribution/metadata.py +19 -0
  170. tensorbored/plugins/graph/__init__.py +0 -0
  171. tensorbored/plugins/graph/graph_util.py +129 -0
  172. tensorbored/plugins/graph/graphs_plugin.py +336 -0
  173. tensorbored/plugins/graph/keras_util.py +328 -0
  174. tensorbored/plugins/graph/metadata.py +42 -0
  175. tensorbored/plugins/histogram/__init__.py +0 -0
  176. tensorbored/plugins/histogram/histograms_plugin.py +144 -0
  177. tensorbored/plugins/histogram/metadata.py +63 -0
  178. tensorbored/plugins/histogram/plugin_data_pb2.py +34 -0
  179. tensorbored/plugins/histogram/summary.py +234 -0
  180. tensorbored/plugins/histogram/summary_v2.py +292 -0
  181. tensorbored/plugins/hparams/__init__.py +14 -0
  182. tensorbored/plugins/hparams/_keras.py +93 -0
  183. tensorbored/plugins/hparams/api.py +130 -0
  184. tensorbored/plugins/hparams/api_pb2.py +208 -0
  185. tensorbored/plugins/hparams/backend_context.py +606 -0
  186. tensorbored/plugins/hparams/download_data.py +158 -0
  187. tensorbored/plugins/hparams/error.py +26 -0
  188. tensorbored/plugins/hparams/get_experiment.py +71 -0
  189. tensorbored/plugins/hparams/hparams_plugin.py +206 -0
  190. tensorbored/plugins/hparams/hparams_util_pb2.py +69 -0
  191. tensorbored/plugins/hparams/json_format_compat.py +38 -0
  192. tensorbored/plugins/hparams/list_metric_evals.py +57 -0
  193. tensorbored/plugins/hparams/list_session_groups.py +1040 -0
  194. tensorbored/plugins/hparams/metadata.py +125 -0
  195. tensorbored/plugins/hparams/metrics.py +41 -0
  196. tensorbored/plugins/hparams/plugin_data_pb2.py +69 -0
  197. tensorbored/plugins/hparams/summary.py +205 -0
  198. tensorbored/plugins/hparams/summary_v2.py +597 -0
  199. tensorbored/plugins/image/__init__.py +0 -0
  200. tensorbored/plugins/image/images_plugin.py +232 -0
  201. tensorbored/plugins/image/metadata.py +65 -0
  202. tensorbored/plugins/image/plugin_data_pb2.py +34 -0
  203. tensorbored/plugins/image/summary.py +159 -0
  204. tensorbored/plugins/image/summary_v2.py +130 -0
  205. tensorbored/plugins/mesh/__init__.py +14 -0
  206. tensorbored/plugins/mesh/mesh_plugin.py +292 -0
  207. tensorbored/plugins/mesh/metadata.py +152 -0
  208. tensorbored/plugins/mesh/plugin_data_pb2.py +37 -0
  209. tensorbored/plugins/mesh/summary.py +251 -0
  210. tensorbored/plugins/mesh/summary_v2.py +214 -0
  211. tensorbored/plugins/metrics/__init__.py +0 -0
  212. tensorbored/plugins/metrics/metadata.py +17 -0
  213. tensorbored/plugins/metrics/metrics_plugin.py +623 -0
  214. tensorbored/plugins/pr_curve/__init__.py +0 -0
  215. tensorbored/plugins/pr_curve/metadata.py +75 -0
  216. tensorbored/plugins/pr_curve/plugin_data_pb2.py +34 -0
  217. tensorbored/plugins/pr_curve/pr_curves_plugin.py +241 -0
  218. tensorbored/plugins/pr_curve/summary.py +574 -0
  219. tensorbored/plugins/profile_redirect/__init__.py +0 -0
  220. tensorbored/plugins/profile_redirect/profile_redirect_plugin.py +49 -0
  221. tensorbored/plugins/projector/__init__.py +67 -0
  222. tensorbored/plugins/projector/metadata.py +26 -0
  223. tensorbored/plugins/projector/projector_config_pb2.py +54 -0
  224. tensorbored/plugins/projector/projector_plugin.py +795 -0
  225. tensorbored/plugins/projector/tf_projector_plugin/index.js +32 -0
  226. tensorbored/plugins/projector/tf_projector_plugin/projector_binary.html +524 -0
  227. tensorbored/plugins/projector/tf_projector_plugin/projector_binary.js +15536 -0
  228. tensorbored/plugins/scalar/__init__.py +0 -0
  229. tensorbored/plugins/scalar/metadata.py +60 -0
  230. tensorbored/plugins/scalar/plugin_data_pb2.py +34 -0
  231. tensorbored/plugins/scalar/scalars_plugin.py +181 -0
  232. tensorbored/plugins/scalar/summary.py +109 -0
  233. tensorbored/plugins/scalar/summary_v2.py +124 -0
  234. tensorbored/plugins/text/__init__.py +0 -0
  235. tensorbored/plugins/text/metadata.py +62 -0
  236. tensorbored/plugins/text/plugin_data_pb2.py +34 -0
  237. tensorbored/plugins/text/summary.py +114 -0
  238. tensorbored/plugins/text/summary_v2.py +124 -0
  239. tensorbored/plugins/text/text_plugin.py +288 -0
  240. tensorbored/plugins/wit_redirect/__init__.py +0 -0
  241. tensorbored/plugins/wit_redirect/wit_redirect_plugin.py +49 -0
  242. tensorbored/program.py +910 -0
  243. tensorbored/summary/__init__.py +35 -0
  244. tensorbored/summary/_output.py +124 -0
  245. tensorbored/summary/_tf/__init__.py +14 -0
  246. tensorbored/summary/_tf/summary/__init__.py +178 -0
  247. tensorbored/summary/_writer.py +105 -0
  248. tensorbored/summary/v1.py +51 -0
  249. tensorbored/summary/v2.py +25 -0
  250. tensorbored/summary/writer/__init__.py +13 -0
  251. tensorbored/summary/writer/event_file_writer.py +291 -0
  252. tensorbored/summary/writer/record_writer.py +50 -0
  253. tensorbored/util/__init__.py +0 -0
  254. tensorbored/util/encoder.py +116 -0
  255. tensorbored/util/grpc_util.py +311 -0
  256. tensorbored/util/img_mime_type_detector.py +40 -0
  257. tensorbored/util/io_util.py +20 -0
  258. tensorbored/util/lazy_tensor_creator.py +110 -0
  259. tensorbored/util/op_evaluator.py +104 -0
  260. tensorbored/util/platform_util.py +20 -0
  261. tensorbored/util/tb_logging.py +24 -0
  262. tensorbored/util/tensor_util.py +617 -0
  263. tensorbored/util/timing.py +122 -0
  264. tensorbored/version.py +21 -0
  265. tensorbored/webfiles.zip +0 -0
  266. tensorbored-2.21.0rc1769983804.dist-info/METADATA +49 -0
  267. tensorbored-2.21.0rc1769983804.dist-info/RECORD +271 -0
  268. tensorbored-2.21.0rc1769983804.dist-info/WHEEL +5 -0
  269. tensorbored-2.21.0rc1769983804.dist-info/entry_points.txt +6 -0
  270. tensorbored-2.21.0rc1769983804.dist-info/licenses/LICENSE +739 -0
  271. tensorbored-2.21.0rc1769983804.dist-info/top_level.txt +1 -0
@@ -0,0 +1,272 @@
1
+ # Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+
16
+ """Contains the implementation for the DirectoryWatcher class."""
17
+
18
+ import bisect
19
+
20
+ from tensorbored.backend.event_processing import io_wrapper
21
+ from tensorbored.compat import tf
22
+ from tensorbored.util import io_util
23
+ from tensorbored.util import tb_logging
24
+
25
+ logger = tb_logging.get_logger()
26
+
27
+
28
+ class DirectoryWatcher:
29
+ """A DirectoryWatcher wraps a loader to load from a sequence of paths.
30
+
31
+ A loader reads a path and produces some kind of values as an iterator. A
32
+ DirectoryWatcher takes a directory, a factory for loaders, and optionally a
33
+ path filter and watches all the paths inside that directory.
34
+
35
+ This class is only valid under the assumption that only one path will be
36
+ written to by the data source at a time and that once the source stops writing
37
+ to a path, it will start writing to a new path that's lexicographically
38
+ greater and never come back. It uses some heuristics to check whether this is
39
+ true based on tracking changes to the files' sizes, but the check can have
40
+ false negatives. However, it should have no false positives.
41
+ """
42
+
43
+ def __init__(self, directory, loader_factory, path_filter=lambda x: True):
44
+ """Constructs a new DirectoryWatcher.
45
+
46
+ Args:
47
+ directory: The directory to load files from.
48
+ loader_factory: A factory for creating loaders. The factory should take a
49
+ path and return an object that has a Load method returning an
50
+ iterator that will yield all events that have not been yielded yet.
51
+ path_filter: If specified, only paths matching this filter are loaded.
52
+
53
+ Raises:
54
+ ValueError: If path_provider or loader_factory are None.
55
+ """
56
+ if directory is None:
57
+ raise ValueError("A directory is required")
58
+ if loader_factory is None:
59
+ raise ValueError("A loader factory is required")
60
+ self._directory = directory
61
+ self._path = None
62
+ self._loader_factory = loader_factory
63
+ self._loader = None
64
+ self._path_filter = path_filter
65
+ self._ooo_writes_detected = False
66
+ # The file size for each file at the time it was finalized.
67
+ self._finalized_sizes = {}
68
+
69
+ def Load(self):
70
+ """Loads new values.
71
+
72
+ The watcher will load from one path at a time; as soon as that path stops
73
+ yielding events, it will move on to the next path. We assume that old paths
74
+ are never modified after a newer path has been written. As a result, Load()
75
+ can be called multiple times in a row without losing events that have not
76
+ been yielded yet. In other words, we guarantee that every event will be
77
+ yielded exactly once.
78
+
79
+ Yields:
80
+ All values that have not been yielded yet.
81
+
82
+ Raises:
83
+ DirectoryDeletedError: If the directory has been permanently deleted
84
+ (as opposed to being temporarily unavailable).
85
+ """
86
+ try:
87
+ for event in self._LoadInternal():
88
+ yield event
89
+ except tf.errors.OpError:
90
+ if not tf.io.gfile.exists(self._directory):
91
+ raise DirectoryDeletedError(
92
+ "Directory %s has been permanently deleted"
93
+ % self._directory
94
+ )
95
+
96
+ def _LoadInternal(self):
97
+ """Internal implementation of Load().
98
+
99
+ The only difference between this and Load() is that the latter will throw
100
+ DirectoryDeletedError on I/O errors if it thinks that the directory has been
101
+ permanently deleted.
102
+
103
+ Yields:
104
+ All values that have not been yielded yet.
105
+ """
106
+
107
+ # If the loader exists, check it for a value.
108
+ if not self._loader:
109
+ self._InitializeLoader()
110
+
111
+ # If it still doesn't exist, there is no data
112
+ if not self._loader:
113
+ return
114
+
115
+ while True:
116
+ # Yield all the new events in the path we're currently loading from.
117
+ for event in self._loader.Load():
118
+ yield event
119
+
120
+ next_path = self._GetNextPath()
121
+ if not next_path:
122
+ logger.info("No path found after %s", self._path)
123
+ # Current path is empty and there are no new paths, so we're done.
124
+ return
125
+
126
+ # There's a new path, so check to make sure there weren't any events
127
+ # written between when we finished reading the current path and when we
128
+ # checked for the new one. The sequence of events might look something
129
+ # like this:
130
+ #
131
+ # 1. Event #1 written to path #1.
132
+ # 2. We check for events and yield event #1 from path #1
133
+ # 3. We check for events and see that there are no more events in path #1.
134
+ # 4. Event #2 is written to path #1.
135
+ # 5. Event #3 is written to path #2.
136
+ # 6. We check for a new path and see that path #2 exists.
137
+ #
138
+ # Without this loop, we would miss event #2. We're also guaranteed by the
139
+ # loader contract that no more events will be written to path #1 after
140
+ # events start being written to path #2, so we don't have to worry about
141
+ # that.
142
+ for event in self._loader.Load():
143
+ yield event
144
+
145
+ logger.info(
146
+ "Directory watcher advancing from %s to %s",
147
+ self._path,
148
+ next_path,
149
+ )
150
+
151
+ # Advance to the next path and start over.
152
+ self._SetPath(next_path)
153
+
154
+ # The number of paths before the current one to check for out of order writes.
155
+ _OOO_WRITE_CHECK_COUNT = 20
156
+
157
+ def OutOfOrderWritesDetected(self):
158
+ """Returns whether any out-of-order writes have been detected.
159
+
160
+ Out-of-order writes are only checked as part of the Load() iterator. Once an
161
+ out-of-order write is detected, this function will always return true.
162
+
163
+ Note that out-of-order write detection is not performed on GCS paths, so
164
+ this function will always return false.
165
+
166
+ Returns:
167
+ Whether any out-of-order write has ever been detected by this watcher.
168
+ """
169
+ return self._ooo_writes_detected
170
+
171
+ def _InitializeLoader(self):
172
+ path = self._GetNextPath()
173
+ if path:
174
+ self._SetPath(path)
175
+
176
+ def _SetPath(self, path):
177
+ """Sets the current path to watch for new events.
178
+
179
+ This also records the size of the old path, if any. If the size can't be
180
+ found, an error is logged.
181
+
182
+ Args:
183
+ path: The full path of the file to watch.
184
+ """
185
+ old_path = self._path
186
+ if old_path and not io_util.IsCloudPath(old_path):
187
+ try:
188
+ # We're done with the path, so store its size.
189
+ size = tf.io.gfile.stat(old_path).length
190
+ logger.debug("Setting latest size of %s to %d", old_path, size)
191
+ self._finalized_sizes[old_path] = size
192
+ except tf.errors.OpError as e:
193
+ logger.error("Unable to get size of %s: %s", old_path, e)
194
+
195
+ self._path = path
196
+ self._loader = self._loader_factory(path)
197
+
198
+ def _GetNextPath(self):
199
+ """Gets the next path to load from.
200
+
201
+ This function also does the checking for out-of-order writes as it iterates
202
+ through the paths.
203
+
204
+ Returns:
205
+ The next path to load events from, or None if there are no more paths.
206
+ """
207
+ paths = sorted(
208
+ path
209
+ for path in io_wrapper.ListDirectoryAbsolute(self._directory)
210
+ if self._path_filter(path)
211
+ )
212
+ if not paths:
213
+ return None
214
+
215
+ if self._path is None:
216
+ return paths[0]
217
+
218
+ # Don't bother checking if the paths are GCS (which we can't check) or if
219
+ # we've already detected an OOO write.
220
+ if not io_util.IsCloudPath(paths[0]) and not self._ooo_writes_detected:
221
+ # Check the previous _OOO_WRITE_CHECK_COUNT paths for out of order writes.
222
+ current_path_index = bisect.bisect_left(paths, self._path)
223
+ ooo_check_start = max(
224
+ 0, current_path_index - self._OOO_WRITE_CHECK_COUNT
225
+ )
226
+ for path in paths[ooo_check_start:current_path_index]:
227
+ if self._HasOOOWrite(path):
228
+ self._ooo_writes_detected = True
229
+ break
230
+
231
+ next_paths = list(
232
+ path for path in paths if self._path is None or path > self._path
233
+ )
234
+ if next_paths:
235
+ return min(next_paths)
236
+ else:
237
+ return None
238
+
239
+ def _HasOOOWrite(self, path):
240
+ """Returns whether the path has had an out-of-order write."""
241
+ # Check the sizes of each path before the current one.
242
+ size = tf.io.gfile.stat(path).length
243
+ old_size = self._finalized_sizes.get(path, None)
244
+ if size != old_size:
245
+ if old_size is None:
246
+ logger.error(
247
+ "File %s created after file %s even though it's "
248
+ "lexicographically earlier",
249
+ path,
250
+ self._path,
251
+ )
252
+ else:
253
+ logger.error(
254
+ "File %s updated even though the current file is %s",
255
+ path,
256
+ self._path,
257
+ )
258
+ return True
259
+ else:
260
+ return False
261
+
262
+
263
+ class DirectoryDeletedError(Exception):
264
+ """Thrown by Load() when the directory is *permanently* gone.
265
+
266
+ We distinguish this from temporary errors so that other code can
267
+ decide to drop all of our data only when a directory has been
268
+ intentionally deleted, as opposed to due to transient filesystem
269
+ errors.
270
+ """
271
+
272
+ pass