chore: import upstream snapshot with attribution
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:14:16 +08:00
commit 8a852e4b4e
36502 changed files with 9277225 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
load("@xla//third_party/rules_python/python:py_library.bzl", "py_library")
load("//tensorflow:tensorflow.default.bzl", "tf_py_strict_test")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:license"],
licenses = ["notice"],
)
py_library(
name = "fake_summary_writer",
testonly = 1,
srcs = ["fake_summary_writer.py"],
strict_deps = True,
visibility = ["//visibility:public"],
deps = [
":writer",
":writer_cache",
"//tensorflow/core:protos_all_py",
"//tensorflow/python/framework:test_lib",
],
)
py_library(
name = "writer",
srcs = ["writer.py"],
strict_deps = True,
visibility = ["//visibility:public"],
deps = [
":event_file_writer",
":event_file_writer_v2",
"//tensorflow/core:protos_all_py",
"//tensorflow/python/eager:context",
"//tensorflow/python/framework:meta_graph",
"//tensorflow/python/framework:ops",
"//tensorflow/python/platform:gfile",
"//tensorflow/python/platform:tf_logging",
"//tensorflow/python/summary:plugin_asset",
"//tensorflow/python/util:tf_export",
],
)
py_library(
name = "event_file_writer",
srcs = ["event_file_writer.py"],
strict_deps = True,
visibility = ["//visibility:public"],
deps = [
"//tensorflow/python/client:_pywrap_events_writer",
"//tensorflow/python/platform:gfile",
"//tensorflow/python/platform:tf_logging",
"//tensorflow/python/util:compat",
],
)
py_library(
name = "writer_cache",
srcs = ["writer_cache.py"],
strict_deps = True,
visibility = ["//visibility:public"],
deps = [
":writer",
"//tensorflow/python/framework:ops",
"//tensorflow/python/util:tf_export",
],
)
py_library(
name = "event_file_writer_v2",
srcs = ["event_file_writer_v2.py"],
strict_deps = True,
visibility = ["//visibility:public"],
deps = [
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:dtypes",
"//tensorflow/python/framework:ops",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:summary_ops_v2",
"//tensorflow/python/platform:gfile",
],
)
tf_py_strict_test(
name = "writer_test",
size = "small",
srcs = [
"writer_test.py",
],
deps = [
":writer",
":writer_cache",
"//tensorflow/core:protos_all_py",
"//tensorflow/python/client:session",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:dtypes",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:meta_graph",
"//tensorflow/python/framework:ops",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:summary_ops_v2",
"//tensorflow/python/platform:client_testlib",
"//tensorflow/python/platform:gfile",
"//tensorflow/python/summary:plugin_asset",
"//tensorflow/python/summary:summary_iterator",
"//tensorflow/python/util:compat",
],
)
@@ -0,0 +1,299 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Writes events to disk in a logdir."""
import collections
import os.path
import sys
import threading
import time
from tensorflow.python.client import _pywrap_events_writer
from tensorflow.python.platform import gfile
from tensorflow.python.platform import tf_logging as logging
from tensorflow.python.util import compat
class EventFileWriter:
"""Writes `Event` protocol buffers to an event file.
The `EventFileWriter` class creates an event file in the specified directory,
and asynchronously writes Event protocol buffers to the file. The Event file
is encoded using the tfrecord format, which is similar to RecordIO.
This class is not thread-safe.
"""
def __init__(self, logdir, max_queue=10, flush_secs=120,
filename_suffix=None):
"""Creates a `EventFileWriter` and an event file to write to.
On construction the summary writer creates a new event file in `logdir`.
This event file will contain `Event` protocol buffers, which are written to
disk via the add_event method.
The other arguments to the constructor control the asynchronous writes to
the event file:
* `flush_secs`: How often, in seconds, to flush the added summaries
and events to disk.
* `max_queue`: Maximum number of summaries or events pending to be
written to disk before one of the 'add' calls block.
Args:
logdir: A string. Directory where event file will be written.
max_queue: Integer. Size of the queue for pending events and summaries.
flush_secs: Number. How often, in seconds, to flush the
pending events and summaries to disk.
filename_suffix: A string. Every event file's name is suffixed with
`filename_suffix`.
"""
self._logdir = str(logdir)
gfile.MakeDirs(self._logdir)
self._max_queue = max_queue
self._flush_secs = flush_secs
self._flush_complete = threading.Event()
self._flush_sentinel = object()
self._close_sentinel = object()
self._ev_writer = _pywrap_events_writer.EventsWriter(
compat.as_bytes(os.path.join(self._logdir, "events")))
if filename_suffix:
self._ev_writer.InitWithSuffix(compat.as_bytes(filename_suffix))
self._initialize()
self._closed = False
def _initialize(self):
"""Initializes or re-initializes the queue and writer thread.
The EventsWriter itself does not need to be re-initialized explicitly,
because it will auto-initialize itself if used after being closed.
"""
self._event_queue = CloseableQueue(self._max_queue)
self._worker = _EventLoggerThread(self._event_queue, self._ev_writer,
self._flush_secs, self._flush_complete,
self._flush_sentinel,
self._close_sentinel)
self._worker.start()
def get_logdir(self):
"""Returns the directory where event file will be written."""
return self._logdir
def reopen(self):
"""Reopens the EventFileWriter.
Can be called after `close()` to add more events in the same directory.
The events will go into a new events file.
Does nothing if the EventFileWriter was not closed.
"""
if self._closed:
self._initialize()
self._closed = False
def add_event(self, event):
"""Adds an event to the event file.
Args:
event: An `Event` protocol buffer.
"""
if not self._closed:
self._try_put(event)
def _try_put(self, item):
"""Attempts to enqueue an item to the event queue.
If the queue is closed, this will close the EventFileWriter and reraise the
exception that caused the queue closure, if one exists.
Args:
item: the item to enqueue
"""
try:
self._event_queue.put(item)
except QueueClosedError:
self._internal_close()
if self._worker.failure_exc_info:
_, exception, _ = self._worker.failure_exc_info
raise exception from None
def flush(self):
"""Flushes the event file to disk.
Call this method to make sure that all pending events have been written to
disk.
"""
if not self._closed:
# Request a flush operation by enqueuing a sentinel and then waiting for
# the writer thread to mark the flush as complete.
self._flush_complete.clear()
self._try_put(self._flush_sentinel)
self._flush_complete.wait()
if self._worker.failure_exc_info:
self._internal_close()
_, exception, _ = self._worker.failure_exc_info
raise exception
def close(self):
"""Flushes the event file to disk and close the file.
Call this method when you do not need the summary writer anymore.
"""
if not self._closed:
self.flush()
self._try_put(self._close_sentinel)
self._internal_close()
def _internal_close(self):
self._closed = True
self._worker.join()
self._ev_writer.Close()
class _EventLoggerThread(threading.Thread):
"""Thread that logs events."""
def __init__(self, queue, ev_writer, flush_secs, flush_complete,
flush_sentinel, close_sentinel):
"""Creates an _EventLoggerThread.
Args:
queue: A CloseableQueue from which to dequeue events. The queue will be
closed just before the thread exits, whether due to `close_sentinel` or
any exception raised in the writing loop.
ev_writer: An event writer. Used to log brain events for
the visualizer.
flush_secs: How often, in seconds, to flush the
pending file to disk.
flush_complete: A threading.Event that will be set whenever a flush
operation requested via `flush_sentinel` has been completed.
flush_sentinel: A sentinel element in queue that tells this thread to
flush the writer and mark the current flush operation complete.
close_sentinel: A sentinel element in queue that tells this thread to
terminate and close the queue.
"""
threading.Thread.__init__(self, name="EventLoggerThread")
self.daemon = True
self._queue = queue
self._ev_writer = ev_writer
self._flush_secs = flush_secs
# The first event will be flushed immediately.
self._next_event_flush_time = 0
self._flush_complete = flush_complete
self._flush_sentinel = flush_sentinel
self._close_sentinel = close_sentinel
# Populated when writing logic raises an exception and kills the thread.
self.failure_exc_info = ()
def run(self):
try:
while True:
event = self._queue.get()
if event is self._close_sentinel:
return
elif event is self._flush_sentinel:
self._ev_writer.Flush()
self._flush_complete.set()
else:
self._ev_writer.WriteEvent(event)
# Flush the event writer every so often.
now = time.time()
if now > self._next_event_flush_time:
self._ev_writer.Flush()
self._next_event_flush_time = now + self._flush_secs
except Exception as e:
logging.error("EventFileWriter writer thread error: %s", e)
self.failure_exc_info = sys.exc_info()
raise
finally:
# When exiting the thread, always complete any pending flush operation
# (to unblock flush() calls) and close the queue (to unblock add_event()
# calls, including those used by flush() and close()), which ensures that
# code using EventFileWriter doesn't deadlock if this thread dies.
self._flush_complete.set()
self._queue.close()
class CloseableQueue:
"""Stripped-down fork of the standard library Queue that is closeable."""
def __init__(self, maxsize=0):
"""Create a queue object with a given maximum size.
Args:
maxsize: int size of queue. If <= 0, the queue size is infinite.
"""
self._maxsize = maxsize
self._queue = collections.deque()
self._closed = False
# Mutex must be held whenever queue is mutating; shared by conditions.
self._mutex = threading.Lock()
# Notify not_empty whenever an item is added to the queue; a
# thread waiting to get is notified then.
self._not_empty = threading.Condition(self._mutex)
# Notify not_full whenever an item is removed from the queue;
# a thread waiting to put is notified then.
self._not_full = threading.Condition(self._mutex)
def get(self):
"""Remove and return an item from the queue.
If the queue is empty, blocks until an item is available.
Returns:
an item from the queue
"""
with self._not_empty:
while not self._queue:
self._not_empty.wait()
item = self._queue.popleft()
self._not_full.notify()
return item
def put(self, item):
"""Put an item into the queue.
If the queue is closed, fails immediately.
If the queue is full, blocks until space is available or until the queue
is closed by a call to close(), at which point this call fails.
Args:
item: an item to add to the queue
Raises:
QueueClosedError: if insertion failed because the queue is closed
"""
with self._not_full:
if self._closed:
raise QueueClosedError()
if self._maxsize > 0:
while len(self._queue) == self._maxsize:
self._not_full.wait()
if self._closed:
raise QueueClosedError()
self._queue.append(item)
self._not_empty.notify()
def close(self):
"""Closes the queue, causing any pending or future `put()` calls to fail."""
with self._not_full:
self._closed = True
self._not_full.notify_all()
class QueueClosedError(Exception):
"""Raised when CloseableQueue.put() fails because the queue is closed."""
@@ -0,0 +1,136 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Writes events to disk in a logdir."""
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import ops
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import summary_ops_v2
from tensorflow.python.platform import gfile
class EventFileWriterV2(object):
"""Writes `Event` protocol buffers to an event file via the graph.
The `EventFileWriterV2` class is backed by the summary file writer in the v2
summary API (currently in tf.contrib.summary), so it uses a shared summary
writer resource and graph ops to write events.
As with the original EventFileWriter, this class will asynchronously write
Event protocol buffers to the backing file. The Event file is encoded using
the tfrecord format, which is similar to RecordIO.
"""
def __init__(self, session, logdir, max_queue=10, flush_secs=120,
filename_suffix=''):
"""Creates an `EventFileWriterV2` and an event file to write to.
On construction, this calls `tf.contrib.summary.create_file_writer` within
the graph from `session.graph` to look up a shared summary writer resource
for `logdir` if one exists, and create one if not. Creating the summary
writer resource in turn creates a new event file in `logdir` to be filled
with `Event` protocol buffers passed to `add_event`. Graph ops to control
this writer resource are added to `session.graph` during this init call;
stateful methods on this class will call `session.run()` on these ops.
Note that because the underlying resource is shared, it is possible that
other parts of the code using the same session may interact independently
with the resource, e.g. by flushing or even closing it. It is the caller's
responsibility to avoid any undesirable sharing in this regard.
The remaining arguments to the constructor (`flush_secs`, `max_queue`, and
`filename_suffix`) control the construction of the shared writer resource
if one is created. If an existing resource is reused, these arguments have
no effect. See `tf.contrib.summary.create_file_writer` for details.
Args:
session: A `tf.compat.v1.Session`. Session that will hold shared writer
resource. The writer ops will be added to session.graph during this
init call.
logdir: A string. Directory where event file will be written.
max_queue: Integer. Size of the queue for pending events and summaries.
flush_secs: Number. How often, in seconds, to flush the
pending events and summaries to disk.
filename_suffix: A string. Every event file's name is suffixed with
`filename_suffix`.
"""
self._session = session
self._logdir = logdir
self._closed = False
gfile.MakeDirs(self._logdir)
with self._session.graph.as_default():
with ops.name_scope('filewriter'):
file_writer = summary_ops_v2.create_file_writer(
logdir=self._logdir,
max_queue=max_queue,
flush_millis=flush_secs * 1000,
filename_suffix=filename_suffix)
with summary_ops_v2.always_record_summaries(), file_writer.as_default():
self._event_placeholder = array_ops.placeholder_with_default(
constant_op.constant('unused', dtypes.string),
shape=[])
self._add_event_op = summary_ops_v2.import_event(
self._event_placeholder)
self._init_op = file_writer.init() # pylint: disable=assignment-from-no-return
self._flush_op = file_writer.flush() # pylint: disable=assignment-from-no-return
self._close_op = file_writer.close() # pylint: disable=assignment-from-no-return
self._session.run(self._init_op)
def get_logdir(self):
"""Returns the directory where event file will be written."""
return self._logdir
def reopen(self):
"""Reopens the EventFileWriter.
Can be called after `close()` to add more events in the same directory.
The events will go into a new events file.
Does nothing if the EventFileWriter was not closed.
"""
if self._closed:
self._closed = False
self._session.run(self._init_op)
def add_event(self, event):
"""Adds an event to the event file.
Args:
event: An `Event` protocol buffer.
"""
if not self._closed:
event_pb = event.SerializeToString()
self._session.run(
self._add_event_op, feed_dict={self._event_placeholder: event_pb})
def flush(self):
"""Flushes the event file to disk.
Call this method to make sure that all pending events have been written to
disk.
"""
self._session.run(self._flush_op)
def close(self):
"""Flushes the event file to disk and close the file.
Call this method when you do not need the summary writer anymore.
"""
if not self._closed:
self.flush()
self._session.run(self._close_op)
self._closed = True
@@ -0,0 +1,139 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Fake summary writer for unit tests."""
from tensorflow.core.framework import summary_pb2
from tensorflow.python.framework import test_util
from tensorflow.python.summary.writer import writer
from tensorflow.python.summary.writer import writer_cache
# TODO(ptucker): Replace with mock framework.
class FakeSummaryWriter(object):
"""Fake summary writer."""
_replaced_summary_writer = None
@classmethod
def install(cls):
if cls._replaced_summary_writer:
raise ValueError('FakeSummaryWriter already installed.')
cls._replaced_summary_writer = writer.FileWriter
writer.FileWriter = FakeSummaryWriter
writer_cache.FileWriter = FakeSummaryWriter
@classmethod
def uninstall(cls):
if not cls._replaced_summary_writer:
raise ValueError('FakeSummaryWriter not installed.')
writer.FileWriter = cls._replaced_summary_writer
writer_cache.FileWriter = cls._replaced_summary_writer
cls._replaced_summary_writer = None
def __init__(self, logdir, graph=None):
self._logdir = logdir
self._graph = graph
self._summaries = {}
self._added_graphs = []
self._added_meta_graphs = []
self._added_session_logs = []
self._added_run_metadata = {}
@property
def summaries(self):
return self._summaries
def assert_summaries(self,
test_case,
expected_logdir=None,
expected_graph=None,
expected_summaries=None,
expected_added_graphs=None,
expected_added_meta_graphs=None,
expected_session_logs=None):
"""Assert expected items have been added to summary writer."""
if expected_logdir is not None:
test_case.assertEqual(expected_logdir, self._logdir)
if expected_graph is not None:
test_case.assertTrue(expected_graph is self._graph)
expected_summaries = expected_summaries or {}
for step in expected_summaries:
test_case.assertTrue(
step in self._summaries,
msg='Missing step %s from %s.' % (step, self._summaries.keys()))
actual_simple_values = {}
for step_summary in self._summaries[step]:
for v in step_summary.value:
# Ignore global_step/sec since it's written by Supervisor in a
# separate thread, so it's non-deterministic how many get written.
if 'global_step/sec' != v.tag:
actual_simple_values[v.tag] = v.simple_value
test_case.assertEqual(expected_summaries[step], actual_simple_values)
if expected_added_graphs is not None:
test_case.assertEqual(expected_added_graphs, self._added_graphs)
if expected_added_meta_graphs is not None:
test_case.assertEqual(len(expected_added_meta_graphs),
len(self._added_meta_graphs))
for expected, actual in zip(expected_added_meta_graphs,
self._added_meta_graphs):
test_util.assert_meta_graph_protos_equal(test_case, expected, actual)
if expected_session_logs is not None:
test_case.assertEqual(expected_session_logs, self._added_session_logs)
def add_summary(self, summ, current_global_step):
"""Add summary."""
if isinstance(summ, bytes):
summary_proto = summary_pb2.Summary()
summary_proto.ParseFromString(summ)
summ = summary_proto
if current_global_step in self._summaries:
step_summaries = self._summaries[current_global_step]
else:
step_summaries = []
self._summaries[current_global_step] = step_summaries
step_summaries.append(summ)
# NOTE: Ignore global_step since its value is non-deterministic.
def add_graph(self, graph, global_step=None, graph_def=None):
"""Add graph."""
if (global_step is not None) and (global_step < 0):
raise ValueError('Invalid global_step %s.' % global_step)
if graph_def is not None:
raise ValueError('Unexpected graph_def %s.' % graph_def)
self._added_graphs.append(graph)
def add_meta_graph(self, meta_graph_def, global_step=None):
"""Add metagraph."""
if (global_step is not None) and (global_step < 0):
raise ValueError('Invalid global_step %s.' % global_step)
self._added_meta_graphs.append(meta_graph_def)
# NOTE: Ignore global_step since its value is non-deterministic.
def add_session_log(self, session_log, global_step=None):
# pylint: disable=unused-argument
self._added_session_logs.append(session_log)
def add_run_metadata(self, run_metadata, tag, global_step=None):
if (global_step is not None) and (global_step < 0):
raise ValueError('Invalid global_step %s.' % global_step)
self._added_run_metadata[tag] = run_metadata
def flush(self):
pass
def reopen(self):
pass
def close(self):
pass
+483
View File
@@ -0,0 +1,483 @@
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Provides an API for generating Event protocol buffers."""
import os.path
import time
import warnings
from tensorflow.core.framework import graph_pb2
from tensorflow.core.framework import summary_pb2
from tensorflow.core.protobuf import meta_graph_pb2
from tensorflow.core.util import event_pb2
from tensorflow.python.eager import context
from tensorflow.python.framework import meta_graph
from tensorflow.python.framework import ops
from tensorflow.python.platform import gfile
from tensorflow.python.platform import tf_logging as logging
from tensorflow.python.summary import plugin_asset
from tensorflow.python.summary.writer.event_file_writer import EventFileWriter
from tensorflow.python.summary.writer.event_file_writer_v2 import EventFileWriterV2
from tensorflow.python.util.tf_export import tf_export
_PLUGINS_DIR = "plugins"
class SummaryToEventTransformer(object):
"""Abstractly implements the SummaryWriter API.
This API basically implements a number of endpoints (add_summary,
add_session_log, etc). The endpoints all generate an event protobuf, which is
passed to the contained event_writer.
"""
def __init__(self, event_writer, graph=None, graph_def=None):
"""Creates a `SummaryWriter` and an event file.
On construction the summary writer creates a new event file in `logdir`.
This event file will contain `Event` protocol buffers constructed when you
call one of the following functions: `add_summary()`, `add_session_log()`,
`add_event()`, or `add_graph()`.
If you pass a `Graph` to the constructor it is added to
the event file. (This is equivalent to calling `add_graph()` later).
TensorBoard will pick the graph from the file and display it graphically so
you can interactively explore the graph you built. You will usually pass
the graph from the session in which you launched it:
```python
...create a graph...
# Launch the graph in a session.
sess = tf.compat.v1.Session()
# Create a summary writer, add the 'graph' to the event file.
writer = tf.compat.v1.summary.FileWriter(<some-directory>, sess.graph)
```
Args:
event_writer: An EventWriter. Implements add_event and get_logdir.
graph: A `Graph` object, such as `sess.graph`.
graph_def: DEPRECATED: Use the `graph` argument instead.
"""
self.event_writer = event_writer
# For storing used tags for session.run() outputs.
self._session_run_tags = {}
if graph is not None or graph_def is not None:
# Calling it with both graph and graph_def for backward compatibility.
self.add_graph(graph=graph, graph_def=graph_def)
# Also export the meta_graph_def in this case.
# graph may itself be a graph_def due to positional arguments
maybe_graph_as_def = (graph.as_graph_def(add_shapes=True)
if isinstance(graph, ops.Graph) else graph)
self.add_meta_graph(
meta_graph.create_meta_graph_def(graph_def=graph_def or
maybe_graph_as_def))
# This set contains tags of Summary Values that have been encountered
# already. The motivation here is that the SummaryWriter only keeps the
# metadata property (which is a SummaryMetadata proto) of the first Summary
# Value encountered for each tag. The SummaryWriter strips away the
# SummaryMetadata for all subsequent Summary Values with tags seen
# previously. This saves space.
self._seen_summary_tags = set()
def add_summary(self, summary, global_step=None):
"""Adds a `Summary` protocol buffer to the event file.
This method wraps the provided summary in an `Event` protocol buffer
and adds it to the event file.
You can pass the result of evaluating any summary op, using
`tf.Session.run` or
`tf.Tensor.eval`, to this
function. Alternatively, you can pass a `tf.compat.v1.Summary` protocol
buffer that you populate with your own data. The latter is
commonly done to report evaluation results in event files.
Args:
summary: A `Summary` protocol buffer, optionally serialized as a string.
global_step: Number. Optional global step value to record with the
summary.
"""
if isinstance(summary, bytes):
summ = summary_pb2.Summary()
summ.ParseFromString(summary)
summary = summ
# We strip metadata from values with tags that we have seen before in order
# to save space - we just store the metadata on the first value with a
# specific tag.
for value in summary.value:
if not value.metadata:
continue
if value.tag in self._seen_summary_tags:
# This tag has been encountered before. Strip the metadata.
value.ClearField("metadata")
continue
# We encounter a value with a tag we have not encountered previously. And
# it has metadata. Remember to strip metadata from future values with this
# tag string.
self._seen_summary_tags.add(value.tag)
event = event_pb2.Event(summary=summary)
self._add_event(event, global_step)
def add_session_log(self, session_log, global_step=None):
"""Adds a `SessionLog` protocol buffer to the event file.
This method wraps the provided session in an `Event` protocol buffer
and adds it to the event file.
Args:
session_log: A `SessionLog` protocol buffer.
global_step: Number. Optional global step value to record with the
summary.
"""
event = event_pb2.Event(session_log=session_log)
self._add_event(event, global_step)
def _add_graph_def(self, graph_def, global_step=None):
graph_bytes = graph_def.SerializeToString()
event = event_pb2.Event(graph_def=graph_bytes)
self._add_event(event, global_step)
def add_graph(self, graph, global_step=None, graph_def=None):
"""Adds a `Graph` to the event file.
The graph described by the protocol buffer will be displayed by
TensorBoard. Most users pass a graph in the constructor instead.
Args:
graph: A `Graph` object, such as `sess.graph`.
global_step: Number. Optional global step counter to record with the
graph.
graph_def: DEPRECATED. Use the `graph` parameter instead.
Raises:
ValueError: If both graph and graph_def are passed to the method.
"""
if graph is not None and graph_def is not None:
raise ValueError("Please pass only graph, or graph_def (deprecated), "
"but not both.")
if isinstance(graph, ops.Graph) or isinstance(graph_def, ops.Graph):
# The user passed a `Graph`.
# Check if the user passed it via the graph or the graph_def argument and
# correct for that.
if not isinstance(graph, ops.Graph):
logging.warning("When passing a `Graph` object, please use the `graph`"
" named argument instead of `graph_def`.")
graph = graph_def
# Serialize the graph with additional info.
true_graph_def = graph.as_graph_def(add_shapes=True)
self._write_plugin_assets(graph)
elif (isinstance(graph, graph_pb2.GraphDef) or
isinstance(graph_def, graph_pb2.GraphDef)):
# The user passed a `GraphDef`.
logging.warning("Passing a `GraphDef` to the SummaryWriter is deprecated."
" Pass a `Graph` object instead, such as `sess.graph`.")
# Check if the user passed it via the graph or the graph_def argument and
# correct for that.
if isinstance(graph, graph_pb2.GraphDef):
true_graph_def = graph
else:
true_graph_def = graph_def
else:
# The user passed neither `Graph`, nor `GraphDef`.
raise TypeError("The passed graph must be an instance of `Graph` "
"or the deprecated `GraphDef`")
# Finally, add the graph_def to the summary writer.
self._add_graph_def(true_graph_def, global_step)
def _write_plugin_assets(self, graph):
plugin_assets = plugin_asset.get_all_plugin_assets(graph)
logdir = self.event_writer.get_logdir()
for asset_container in plugin_assets:
plugin_name = asset_container.plugin_name
plugin_dir = os.path.join(logdir, _PLUGINS_DIR, plugin_name)
gfile.MakeDirs(plugin_dir)
assets = asset_container.assets()
for (asset_name, content) in assets.items():
asset_path = os.path.join(plugin_dir, asset_name)
with gfile.Open(asset_path, "w") as f:
f.write(content)
def add_meta_graph(self, meta_graph_def, global_step=None):
"""Adds a `MetaGraphDef` to the event file.
The `MetaGraphDef` allows running the given graph via
`saver.import_meta_graph()`.
Args:
meta_graph_def: A `MetaGraphDef` object, often as returned by
`saver.export_meta_graph()`.
global_step: Number. Optional global step counter to record with the
graph.
Raises:
TypeError: If both `meta_graph_def` is not an instance of `MetaGraphDef`.
"""
if not isinstance(meta_graph_def, meta_graph_pb2.MetaGraphDef):
raise TypeError("meta_graph_def must be type MetaGraphDef, saw type: %s" %
type(meta_graph_def))
meta_graph_bytes = meta_graph_def.SerializeToString()
event = event_pb2.Event(meta_graph_def=meta_graph_bytes)
self._add_event(event, global_step)
def add_run_metadata(self, run_metadata, tag, global_step=None):
"""Adds a metadata information for a single session.run() call.
Args:
run_metadata: A `RunMetadata` protobuf object.
tag: The tag name for this metadata.
global_step: Number. Optional global step counter to record with the
StepStats.
Raises:
ValueError: If the provided tag was already used for this type of event.
"""
if tag in self._session_run_tags:
raise ValueError("The provided tag was already used for this event type")
self._session_run_tags[tag] = True
tagged_metadata = event_pb2.TaggedRunMetadata()
tagged_metadata.tag = tag
# Store the `RunMetadata` object as bytes in order to have postponed
# (lazy) deserialization when used later.
tagged_metadata.run_metadata = run_metadata.SerializeToString()
event = event_pb2.Event(tagged_run_metadata=tagged_metadata)
self._add_event(event, global_step)
def _add_event(self, event, step):
event.wall_time = time.time()
if step is not None:
event.step = int(step)
self.event_writer.add_event(event)
@tf_export(v1=["summary.FileWriter"])
class FileWriter(SummaryToEventTransformer):
"""Writes `Summary` protocol buffers to event files.
The `FileWriter` class provides a mechanism to create an event file in a
given directory and add summaries and events to it. The class updates the
file contents asynchronously. This allows a training program to call methods
to add data to the file directly from the training loop, without slowing down
training.
When constructed with a `tf.compat.v1.Session` parameter, a `FileWriter`
instead forms a compatibility layer over new graph-based summaries
to facilitate the use of new summary writing with
pre-existing code that expects a `FileWriter` instance.
This class is not thread-safe.
@compatibility(TF2)
This API is not compatible with eager execution or `tf.function`. To migrate
to TF2, please use `tf.summary.create_file_writer` instead for summary
management. To specify the summary step, you can manage the context with
`tf.summary.SummaryWriter`, which is returned by
`tf.summary.create_file_writer()`. Or, you can also use the `step` argument
of summary functions such as `tf.summary.histogram`.
See the usage example shown below.
For a comprehensive `tf.summary` migration guide, please follow
[Migrating tf.summary usage to
TF 2.0](https://www.tensorflow.org/tensorboard/migrate#in_tf_1x).
#### How to Map Arguments
| TF1 Arg Name | TF2 Arg Name | Note |
| :---------------- | :---------------- | :-------------------------------- |
| `logdir` | `logdir` | - |
| `graph` | Not supported | - |
| `max_queue` | `max_queue` | - |
| `flush_secs` | `flush_millis` | The unit of time is changed |
: : : from seconds to milliseconds. :
| `graph_def` | Not supported | - |
| `filename_suffix` | `filename_suffix` | - |
| `name` | `name` | - |
#### TF1 & TF2 Usage Example
TF1:
```python
dist = tf.compat.v1.placeholder(tf.float32, [100])
tf.compat.v1.summary.histogram(name="distribution", values=dist)
writer = tf.compat.v1.summary.FileWriter("/tmp/tf1_summary_example")
summaries = tf.compat.v1.summary.merge_all()
sess = tf.compat.v1.Session()
for step in range(100):
mean_moving_normal = np.random.normal(loc=step, scale=1, size=[100])
summ = sess.run(summaries, feed_dict={dist: mean_moving_normal})
writer.add_summary(summ, global_step=step)
```
TF2:
```python
writer = tf.summary.create_file_writer("/tmp/tf2_summary_example")
for step in range(100):
mean_moving_normal = np.random.normal(loc=step, scale=1, size=[100])
with writer.as_default(step=step):
tf.summary.histogram(name='distribution', data=mean_moving_normal)
```
@end_compatibility
"""
def __init__(self,
logdir,
graph=None,
max_queue=10,
flush_secs=120,
graph_def=None,
filename_suffix=None,
session=None):
"""Creates a `FileWriter`, optionally shared within the given session.
Typically, constructing a file writer creates a new event file in `logdir`.
This event file will contain `Event` protocol buffers constructed when you
call one of the following functions: `add_summary()`, `add_session_log()`,
`add_event()`, or `add_graph()`.
If you pass a `Graph` to the constructor it is added to
the event file. (This is equivalent to calling `add_graph()` later).
TensorBoard will pick the graph from the file and display it graphically so
you can interactively explore the graph you built. You will usually pass
the graph from the session in which you launched it:
```python
...create a graph...
# Launch the graph in a session.
sess = tf.compat.v1.Session()
# Create a summary writer, add the 'graph' to the event file.
writer = tf.compat.v1.summary.FileWriter(<some-directory>, sess.graph)
```
The `session` argument to the constructor makes the returned `FileWriter` a
compatibility layer over new graph-based summaries (`tf.summary`).
Crucially, this means the underlying writer resource and events file will
be shared with any other `FileWriter` using the same `session` and `logdir`.
In either case, ops will be added to `session.graph` to control the
underlying file writer resource.
Args:
logdir: A string. Directory where event file will be written.
graph: A `Graph` object, such as `sess.graph`.
max_queue: Integer. Size of the queue for pending events and summaries.
flush_secs: Number. How often, in seconds, to flush the
pending events and summaries to disk.
graph_def: DEPRECATED: Use the `graph` argument instead.
filename_suffix: A string. Every event file's name is suffixed with
`suffix`.
session: A `tf.compat.v1.Session` object. See details above.
Raises:
RuntimeError: If called with eager execution enabled.
@compatibility(eager)
`v1.summary.FileWriter` is not compatible with eager execution.
To write TensorBoard summaries under eager execution,
use `tf.summary.create_file_writer` or
a `with v1.Graph().as_default():` context.
@end_compatibility
"""
if context.executing_eagerly():
raise RuntimeError(
"v1.summary.FileWriter is not compatible with eager execution. "
"Use `tf.summary.create_file_writer`,"
"or a `with v1.Graph().as_default():` context")
if session is not None:
event_writer = EventFileWriterV2(
session, logdir, max_queue, flush_secs, filename_suffix)
else:
event_writer = EventFileWriter(logdir, max_queue, flush_secs,
filename_suffix)
self._closed = False
super(FileWriter, self).__init__(event_writer, graph, graph_def)
def __enter__(self):
"""Make usable with "with" statement."""
return self
def __exit__(self, unused_type, unused_value, unused_traceback):
"""Make usable with "with" statement."""
self.close()
def get_logdir(self):
"""Returns the directory where event file will be written."""
return self.event_writer.get_logdir()
def _warn_if_event_writer_is_closed(self):
if self._closed:
warnings.warn("Attempting to use a closed FileWriter. "
"The operation will be a noop unless the FileWriter "
"is explicitly reopened.")
def _add_event(self, event, step):
self._warn_if_event_writer_is_closed()
super(FileWriter, self)._add_event(event, step)
def add_event(self, event):
"""Adds an event to the event file.
Args:
event: An `Event` protocol buffer.
"""
self._warn_if_event_writer_is_closed()
self.event_writer.add_event(event)
def flush(self):
"""Flushes the event file to disk.
Call this method to make sure that all pending events have been written to
disk.
"""
# Flushing a closed EventFileWriterV2 raises an exception. It is,
# however, a noop for EventFileWriter.
self._warn_if_event_writer_is_closed()
self.event_writer.flush()
def close(self):
"""Flushes the event file to disk and close the file.
Call this method when you do not need the summary writer anymore.
"""
self.event_writer.close()
self._closed = True
def reopen(self):
"""Reopens the EventFileWriter.
Can be called after `close()` to add more events in the same directory.
The events will go into a new events file.
Does nothing if the EventFileWriter was not closed.
"""
self.event_writer.reopen()
self._closed = False
@@ -0,0 +1,60 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""A cache for FileWriters."""
import threading
from tensorflow.python.framework import ops
from tensorflow.python.summary.writer.writer import FileWriter
from tensorflow.python.util.tf_export import tf_export
@tf_export(v1=['summary.FileWriterCache'])
class FileWriterCache(object):
"""Cache for file writers.
This class caches file writers, one per directory.
"""
# Cache, keyed by directory.
_cache = {}
# Lock protecting _FILE_WRITERS.
_lock = threading.RLock()
@staticmethod
def clear():
"""Clear cached summary writers. Currently only used for unit tests."""
with FileWriterCache._lock:
# Make sure all the writers are closed now (otherwise open file handles
# may hang around, blocking deletions on Windows).
for item in FileWriterCache._cache.values():
item.close()
FileWriterCache._cache = {}
@staticmethod
def get(logdir):
"""Returns the FileWriter for the specified directory.
Args:
logdir: str, name of the directory.
Returns:
A `FileWriter`.
"""
with FileWriterCache._lock:
if logdir not in FileWriterCache._cache:
FileWriterCache._cache[logdir] = FileWriter(
logdir, graph=ops.get_default_graph())
return FileWriterCache._cache[logdir]
@@ -0,0 +1,748 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for tensorflow.python.summary.writer."""
import glob
import os.path
import shutil
import threading
import time
import warnings
from tensorflow.core.framework import graph_pb2
from tensorflow.core.framework import summary_pb2
from tensorflow.core.protobuf import config_pb2
from tensorflow.core.protobuf import meta_graph_pb2
from tensorflow.core.util import event_pb2
from tensorflow.core.util.event_pb2 import SessionLog
from tensorflow.python.client import session
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import errors_impl
from tensorflow.python.framework import meta_graph
from tensorflow.python.framework import ops
from tensorflow.python.framework import test_util
from tensorflow.python.ops import summary_ops_v2
from tensorflow.python.platform import gfile
from tensorflow.python.platform import test
from tensorflow.python.summary import plugin_asset
from tensorflow.python.summary import summary_iterator
from tensorflow.python.summary.writer import writer
from tensorflow.python.summary.writer import writer_cache
from tensorflow.python.util import compat
class FileWriterTestBase:
def _FileWriter(self, *args, **kwargs):
return writer.FileWriter(*args, **kwargs)
def _TestDir(self, test_name):
test_dir = os.path.join(self.get_temp_dir(), test_name)
return test_dir
def _CleanTestDir(self, test_name):
test_dir = self._TestDir(test_name)
if os.path.exists(test_dir):
shutil.rmtree(test_dir)
return test_dir
def _EventsReader(self, test_dir):
event_paths = glob.glob(os.path.join(test_dir, "event*"))
# If the tests runs multiple times in the same directory we can have
# more than one matching event file. We only want to read the last one.
self.assertTrue(event_paths)
return summary_iterator.summary_iterator(event_paths[-1])
def assertRecent(self, t):
# We want to ensure the timestamp is something plausible, and aren't able
# to mock out the actual clock used through many layers of the stack, so
# just assert that it's within the past hour, which should always be true.
self.assertLessEqual(t, time.time())
self.assertLess(abs(t - time.time()), 3600)
def assertEventsWithGraph(self, test_dir, g, has_shapes):
meta_graph_def = meta_graph.create_meta_graph_def(
graph_def=g.as_graph_def(add_shapes=has_shapes))
rr = self._EventsReader(test_dir)
# The first event should list the file_version.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual("brain.Event:2", ev.file_version)
# The next event should have the graph.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual(0, ev.step)
ev_graph = graph_pb2.GraphDef()
ev_graph.ParseFromString(ev.graph_def)
self.assertProtoEquals(g.as_graph_def(add_shapes=has_shapes), ev_graph)
# The next event should have the metagraph.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual(0, ev.step)
ev_meta_graph = meta_graph_pb2.MetaGraphDef()
ev_meta_graph.ParseFromString(ev.meta_graph_def)
self.assertProtoEquals(meta_graph_def, ev_meta_graph)
# We should be done.
self.assertRaises(StopIteration, lambda: next(rr))
@test_util.run_deprecated_v1
def testAddingSummaryGraphAndRunMetadata(self):
test_dir = self._CleanTestDir("basics")
sw = self._FileWriter(test_dir)
sw.add_session_log(event_pb2.SessionLog(status=SessionLog.START), 1)
sw.add_summary(
summary_pb2.Summary(
value=[summary_pb2.Summary.Value(
tag="mee", simple_value=10.0)]),
10)
sw.add_summary(
summary_pb2.Summary(
value=[summary_pb2.Summary.Value(
tag="boo", simple_value=20.0)]),
20)
with ops.Graph().as_default() as g:
constant_op.constant([0], name="zero")
sw.add_graph(g, global_step=30)
run_metadata = config_pb2.RunMetadata()
device_stats = run_metadata.step_stats.dev_stats.add()
device_stats.device = "test"
sw.add_run_metadata(run_metadata, "test run", global_step=40)
sw.close()
rr = self._EventsReader(test_dir)
# The first event should list the file_version.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual("brain.Event:2", ev.file_version)
# The next event should be the START message.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual(1, ev.step)
self.assertEqual(SessionLog.START, ev.session_log.status)
# The next event should have the value 'mee=10.0'.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual(10, ev.step)
self.assertProtoEquals("""
value { tag: 'mee' simple_value: 10.0 }
""", ev.summary)
# The next event should have the value 'boo=20.0'.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual(20, ev.step)
self.assertProtoEquals("""
value { tag: 'boo' simple_value: 20.0 }
""", ev.summary)
# The next event should have the graph_def.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual(30, ev.step)
ev_graph = graph_pb2.GraphDef()
ev_graph.ParseFromString(ev.graph_def)
self.assertProtoEquals(g.as_graph_def(add_shapes=True), ev_graph)
# The next event should have metadata for the run.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual(40, ev.step)
self.assertEqual("test run", ev.tagged_run_metadata.tag)
parsed_run_metadata = config_pb2.RunMetadata()
parsed_run_metadata.ParseFromString(ev.tagged_run_metadata.run_metadata)
self.assertProtoEquals(run_metadata, parsed_run_metadata)
# We should be done.
self.assertRaises(StopIteration, lambda: next(rr))
@test_util.run_deprecated_v1
def testGraphAsNamed(self):
test_dir = self._CleanTestDir("basics_named_graph")
with ops.Graph().as_default() as g:
constant_op.constant([12], name="douze")
sw = self._FileWriter(test_dir, graph=g)
sw.close()
self.assertEventsWithGraph(test_dir, g, True)
@test_util.run_deprecated_v1
def testGraphAsPositional(self):
test_dir = self._CleanTestDir("basics_positional_graph")
with ops.Graph().as_default() as g:
constant_op.constant([12], name="douze")
sw = self._FileWriter(test_dir, g)
sw.close()
self.assertEventsWithGraph(test_dir, g, True)
@test_util.run_deprecated_v1
def testGraphDefAsNamed(self):
test_dir = self._CleanTestDir("basics_named_graph_def")
with ops.Graph().as_default() as g:
constant_op.constant([12], name="douze")
gd = g.as_graph_def()
sw = self._FileWriter(test_dir, graph_def=gd)
sw.close()
self.assertEventsWithGraph(test_dir, g, False)
@test_util.run_deprecated_v1
def testGraphDefAsPositional(self):
test_dir = self._CleanTestDir("basics_positional_graph_def")
with ops.Graph().as_default() as g:
constant_op.constant([12], name="douze")
gd = g.as_graph_def()
sw = self._FileWriter(test_dir, gd)
sw.close()
self.assertEventsWithGraph(test_dir, g, False)
@test_util.run_deprecated_v1
def testGraphAndGraphDef(self):
with self.assertRaises(ValueError):
test_dir = self._CleanTestDir("basics_graph_and_graph_def")
with ops.Graph().as_default() as g:
constant_op.constant([12], name="douze")
gd = g.as_graph_def()
sw = self._FileWriter(test_dir, graph=g, graph_def=gd)
sw.close()
@test_util.run_deprecated_v1
def testNeitherGraphNorGraphDef(self):
with self.assertRaises(TypeError):
test_dir = self._CleanTestDir("basics_string_instead_of_graph")
sw = self._FileWriter(test_dir, "string instead of graph object")
sw.close()
@test_util.run_deprecated_v1
def testCloseAndReopen(self):
test_dir = self._CleanTestDir("close_and_reopen")
sw = self._FileWriter(test_dir)
sw.add_session_log(event_pb2.SessionLog(status=SessionLog.START), 1)
sw.close()
# Sleep at least one second to make sure we get a new event file name.
time.sleep(1.2)
sw.reopen()
sw.add_session_log(event_pb2.SessionLog(status=SessionLog.START), 2)
sw.close()
# We should now have 2 events files.
event_paths = sorted(glob.glob(os.path.join(test_dir, "event*")))
self.assertEqual(2, len(event_paths))
# Check the first file contents.
rr = summary_iterator.summary_iterator(event_paths[0])
# The first event should list the file_version.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual("brain.Event:2", ev.file_version)
# The next event should be the START message.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual(1, ev.step)
self.assertEqual(SessionLog.START, ev.session_log.status)
# We should be done.
self.assertRaises(StopIteration, lambda: next(rr))
# Check the second file contents.
rr = summary_iterator.summary_iterator(event_paths[1])
# The first event should list the file_version.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual("brain.Event:2", ev.file_version)
# The next event should be the START message.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual(2, ev.step)
self.assertEqual(SessionLog.START, ev.session_log.status)
# We should be done.
self.assertRaises(StopIteration, lambda: next(rr))
@test_util.run_deprecated_v1
def testNonBlockingClose(self):
test_dir = self._CleanTestDir("non_blocking_close")
sw = self._FileWriter(test_dir)
# Sleep 1.2 seconds to make sure event queue is empty.
time.sleep(1.2)
time_before_close = time.time()
sw.close()
self.assertRecent(time_before_close)
@test_util.run_deprecated_v1
def testUseAfterClose(self):
test_dir = self._CleanTestDir("use_after_close")
sw = self._FileWriter(test_dir)
sw.close()
with warnings.catch_warnings(record=True) as triggered:
warnings.simplefilter("always")
self.assertFalse(triggered)
sw.add_summary(summary_pb2.Summary())
sw.add_session_log(event_pb2.SessionLog())
sw.add_graph(ops.Graph())
self.assertEqual(len(triggered), 3)
for w in triggered:
self.assertEqual(w.category, UserWarning)
@test_util.run_deprecated_v1
def testWithStatement(self):
test_dir = self._CleanTestDir("with_statement")
with self._FileWriter(test_dir) as sw:
sw.add_session_log(event_pb2.SessionLog(status=SessionLog.START), 1)
event_paths = sorted(glob.glob(os.path.join(test_dir, "event*")))
self.assertEqual(1, len(event_paths))
# Checks that values returned from session Run() calls are added correctly to
# summaries. These are numpy types so we need to check they fit in the
# protocol buffers correctly.
@test_util.run_deprecated_v1
def testAddingSummariesFromSessionRunCalls(self):
test_dir = self._CleanTestDir("global_step")
sw = self._FileWriter(test_dir)
with self.cached_session():
i = constant_op.constant(1, dtype=dtypes.int32, shape=[])
l = constant_op.constant(2, dtype=dtypes.int64, shape=[])
# Test the summary can be passed serialized.
summ = summary_pb2.Summary(
value=[summary_pb2.Summary.Value(
tag="i", simple_value=1.0)])
sw.add_summary(summ.SerializeToString(), self.evaluate(i))
sw.add_summary(
summary_pb2.Summary(
value=[summary_pb2.Summary.Value(tag="l", simple_value=2.0)]),
self.evaluate(l))
sw.close()
rr = self._EventsReader(test_dir)
# File_version.
ev = next(rr)
self.assertTrue(ev)
self.assertRecent(ev.wall_time)
self.assertEqual("brain.Event:2", ev.file_version)
# Summary passed serialized.
ev = next(rr)
self.assertTrue(ev)
self.assertRecent(ev.wall_time)
self.assertEqual(1, ev.step)
self.assertProtoEquals("""
value { tag: 'i' simple_value: 1.0 }
""", ev.summary)
# Summary passed as SummaryObject.
ev = next(rr)
self.assertTrue(ev)
self.assertRecent(ev.wall_time)
self.assertEqual(2, ev.step)
self.assertProtoEquals("""
value { tag: 'l' simple_value: 2.0 }
""", ev.summary)
# We should be done.
self.assertRaises(StopIteration, lambda: next(rr))
@test_util.run_deprecated_v1
def testPluginMetadataStrippedFromSubsequentEvents(self):
test_dir = self._CleanTestDir("basics")
sw = self._FileWriter(test_dir)
sw.add_session_log(event_pb2.SessionLog(status=SessionLog.START), 1)
# We add 2 summaries with the same tags. They both have metadata. The writer
# should strip the metadata from the second one.
value = summary_pb2.Summary.Value(tag="foo", simple_value=10.0)
value.metadata.plugin_data.plugin_name = "bar"
value.metadata.plugin_data.content = compat.as_bytes("... content ...")
sw.add_summary(summary_pb2.Summary(value=[value]), 10)
value = summary_pb2.Summary.Value(tag="foo", simple_value=10.0)
value.metadata.plugin_data.plugin_name = "bar"
value.metadata.plugin_data.content = compat.as_bytes("... content ...")
sw.add_summary(summary_pb2.Summary(value=[value]), 10)
sw.close()
rr = self._EventsReader(test_dir)
# The first event should list the file_version.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual("brain.Event:2", ev.file_version)
# The next event should be the START message.
ev = next(rr)
self.assertRecent(ev.wall_time)
self.assertEqual(1, ev.step)
self.assertEqual(SessionLog.START, ev.session_log.status)
# This is the first event with tag foo. It should contain SummaryMetadata.
ev = next(rr)
self.assertProtoEquals("""
value {
tag: "foo"
simple_value: 10.0
metadata {
plugin_data {
plugin_name: "bar"
content: "... content ..."
}
}
}
""", ev.summary)
# This is the second event with tag foo. It should lack SummaryMetadata
# because the file writer should have stripped it.
ev = next(rr)
self.assertProtoEquals("""
value {
tag: "foo"
simple_value: 10.0
}
""", ev.summary)
# We should be done.
self.assertRaises(StopIteration, lambda: next(rr))
@test_util.run_deprecated_v1
def testFileWriterWithSuffix(self):
test_dir = self._CleanTestDir("test_suffix")
sw = self._FileWriter(test_dir, filename_suffix="_test_suffix")
for _ in range(10):
sw.add_summary(
summary_pb2.Summary(value=[
summary_pb2.Summary.Value(tag="float_ten", simple_value=10.0)
]),
10)
sw.close()
sw.reopen()
sw.close()
event_filenames = glob.glob(os.path.join(test_dir, "event*"))
for filename in event_filenames:
self.assertTrue(filename.endswith("_test_suffix"))
def testPluginAssetSerialized(self):
class ExamplePluginAsset(plugin_asset.PluginAsset):
plugin_name = "example"
def assets(self):
return {"foo.txt": "foo!", "bar.txt": "bar!"}
with ops.Graph().as_default() as g:
plugin_asset.get_plugin_asset(ExamplePluginAsset)
logdir = self.get_temp_dir()
fw = self._FileWriter(logdir)
fw.add_graph(g)
plugin_dir = os.path.join(logdir, writer._PLUGINS_DIR, "example")
with gfile.Open(os.path.join(plugin_dir, "foo.txt"), "r") as f:
content = f.read()
self.assertEqual(content, "foo!")
with gfile.Open(os.path.join(plugin_dir, "bar.txt"), "r") as f:
content = f.read()
self.assertEqual(content, "bar!")
class FakeWriteError(Exception):
pass
class FileWriterTestCase(FileWriterTestBase, test.TestCase):
@test_util.run_deprecated_v1
def testWriterException_raisedFromFlush(self):
test_dir = self.get_temp_dir()
sw = self._FileWriter(test_dir)
writer_thread = sw.event_writer._worker
with test.mock.patch.object(
writer_thread, "_ev_writer", autospec=True) as mock_writer:
# Coordinate threads to ensure both events are added before the writer
# thread dies, to avoid the second add_event() failing instead of flush().
second_event_added = threading.Event()
def _FakeWriteEvent(event):
del event # unused
second_event_added.wait()
raise FakeWriteError()
mock_writer.WriteEvent.side_effect = _FakeWriteEvent
sw.add_event(event_pb2.Event())
sw.add_event(event_pb2.Event())
second_event_added.set()
with self.assertRaises(FakeWriteError):
sw.flush()
@test_util.run_deprecated_v1
def testWriterException_raisedFromClose(self):
test_dir = self.get_temp_dir()
sw = self._FileWriter(test_dir)
writer_thread = sw.event_writer._worker
with test.mock.patch.object(
writer_thread, "_ev_writer", autospec=True) as mock_writer:
mock_writer.WriteEvent.side_effect = FakeWriteError()
sw.add_event(event_pb2.Event())
with self.assertRaises(FakeWriteError):
sw.close()
@test_util.run_deprecated_v1
def testWriterException_raisedFromAddEvent(self):
test_dir = self.get_temp_dir()
sw = self._FileWriter(test_dir)
writer_thread = sw.event_writer._worker
with test.mock.patch.object(
writer_thread, "_ev_writer", autospec=True) as mock_writer:
mock_writer.WriteEvent.side_effect = FakeWriteError()
sw.add_event(event_pb2.Event())
# Wait for writer thread to exit first, then try to add a new event.
writer_thread.join()
with self.assertRaises(FakeWriteError):
sw.add_event(event_pb2.Event())
@test_util.run_deprecated_v1
def testWriterException_raisedFromPendingAddEvent(self):
test_dir = self.get_temp_dir()
# Set max_queue=1 to allow the third add_event() call to block (first event
# is consumed immediately, the second fills the queue, the third blocks).
sw = self._FileWriter(test_dir, max_queue=1)
writer_thread = sw.event_writer._worker
with test.mock.patch.object(
writer_thread, "_ev_writer", autospec=True) as mock_writer:
# Coordinate threads to ensure the first two events are added and then
# the writer thread sleeps briefly before exiting, to maximize the chance
# that the third add_event() reaches the pending blocked state before the
# queue closes on writer thread exit, since that's what we want to test.
second_event_added = threading.Event()
def _FakeWriteEvent(event):
del event # unused
second_event_added.wait()
time.sleep(0.1)
raise FakeWriteError()
mock_writer.WriteEvent.side_effect = _FakeWriteEvent
sw.add_event(event_pb2.Event())
sw.add_event(event_pb2.Event())
second_event_added.set()
with self.assertRaises(FakeWriteError):
sw.add_event(event_pb2.Event())
class SessionBasedFileWriterTestCase(FileWriterTestBase, test.TestCase):
"""Tests for FileWriter behavior when passed a Session argument."""
def _FileWriter(self, *args, **kwargs):
if "session" not in kwargs:
# Pass in test_session() as the session. It will be cached during this
# test method invocation so that any other use of test_session() with no
# graph should result in re-using the same underlying Session.
with self.cached_session() as sess:
kwargs["session"] = sess
return writer.FileWriter(*args, **kwargs)
return writer.FileWriter(*args, **kwargs)
def _createTaggedSummary(self, tag):
summary = summary_pb2.Summary()
summary.value.add(tag=tag)
return summary
def testSharing_withOtherSessionBasedFileWriters(self):
logdir = self.get_temp_dir()
with session.Session() as sess:
# Initial file writer
writer1 = writer.FileWriter(session=sess, logdir=logdir)
writer1.add_summary(self._createTaggedSummary("one"), 1)
writer1.flush()
# File writer, should share file with writer1
writer2 = writer.FileWriter(session=sess, logdir=logdir)
writer2.add_summary(self._createTaggedSummary("two"), 2)
writer2.flush()
# File writer with different logdir (shouldn't be in this logdir at all)
writer3 = writer.FileWriter(session=sess, logdir=logdir + "-other")
writer3.add_summary(self._createTaggedSummary("three"), 3)
writer3.flush()
# File writer in a different session (should be in separate file)
time.sleep(1.1) # Ensure filename has a different timestamp
with session.Session() as other_sess:
writer4 = writer.FileWriter(session=other_sess, logdir=logdir)
writer4.add_summary(self._createTaggedSummary("four"), 4)
writer4.flush()
# One more file writer, should share file with writer1
writer5 = writer.FileWriter(session=sess, logdir=logdir)
writer5.add_summary(self._createTaggedSummary("five"), 5)
writer5.flush()
event_paths = iter(sorted(glob.glob(os.path.join(logdir, "event*"))))
# First file should have tags "one", "two", and "five"
events = summary_iterator.summary_iterator(next(event_paths))
self.assertEqual("brain.Event:2", next(events).file_version)
self.assertEqual("one", next(events).summary.value[0].tag)
self.assertEqual("two", next(events).summary.value[0].tag)
self.assertEqual("five", next(events).summary.value[0].tag)
self.assertRaises(StopIteration, lambda: next(events))
# Second file should have just "four"
events = summary_iterator.summary_iterator(next(event_paths))
self.assertEqual("brain.Event:2", next(events).file_version)
self.assertEqual("four", next(events).summary.value[0].tag)
self.assertRaises(StopIteration, lambda: next(events))
# No more files
self.assertRaises(StopIteration, lambda: next(event_paths))
# Just check that the other logdir file exists to be sure we wrote it
self.assertTrue(glob.glob(os.path.join(logdir + "-other", "event*")))
def testSharing_withExplicitSummaryFileWriters(self):
logdir = self.get_temp_dir()
with session.Session() as sess:
# Initial file writer via FileWriter(session=?)
writer1 = writer.FileWriter(session=sess, logdir=logdir)
writer1.add_summary(self._createTaggedSummary("one"), 1)
writer1.flush()
# Next one via create_file_writer(), should use same file
writer2 = summary_ops_v2.create_file_writer(logdir=logdir)
with summary_ops_v2.always_record_summaries(), writer2.as_default():
summary2 = summary_ops_v2.scalar("two", 2.0, step=2)
sess.run(writer2.init())
sess.run(summary2)
sess.run(writer2.flush())
# Next has different shared name, should be in separate file
time.sleep(1.1) # Ensure filename has a different timestamp
writer3 = summary_ops_v2.create_file_writer(logdir=logdir, name="other")
with summary_ops_v2.always_record_summaries(), writer3.as_default():
summary3 = summary_ops_v2.scalar("three", 3.0, step=3)
sess.run(writer3.init())
sess.run(summary3)
sess.run(writer3.flush())
# Next uses a second session, should be in separate file
time.sleep(1.1) # Ensure filename has a different timestamp
with session.Session() as other_sess:
writer4 = summary_ops_v2.create_file_writer(logdir=logdir)
with summary_ops_v2.always_record_summaries(), writer4.as_default():
summary4 = summary_ops_v2.scalar("four", 4.0, step=4)
other_sess.run(writer4.init())
other_sess.run(summary4)
other_sess.run(writer4.flush())
# Next via FileWriter(session=?) uses same second session, should be in
# same separate file. (This checks sharing in the other direction)
writer5 = writer.FileWriter(session=other_sess, logdir=logdir)
writer5.add_summary(self._createTaggedSummary("five"), 5)
writer5.flush()
# One more via create_file_writer(), should use same file
writer6 = summary_ops_v2.create_file_writer(logdir=logdir)
with summary_ops_v2.always_record_summaries(), writer6.as_default():
summary6 = summary_ops_v2.scalar("six", 6.0, step=6)
sess.run(writer6.init())
sess.run(summary6)
sess.run(writer6.flush())
event_paths = iter(sorted(glob.glob(os.path.join(logdir, "event*"))))
# First file should have tags "one", "two", and "six"
events = summary_iterator.summary_iterator(next(event_paths))
self.assertEqual("brain.Event:2", next(events).file_version)
self.assertEqual("one", next(events).summary.value[0].tag)
self.assertEqual("two", next(events).summary.value[0].tag)
self.assertEqual("six", next(events).summary.value[0].tag)
self.assertRaises(StopIteration, lambda: next(events))
# Second file should have just "three"
events = summary_iterator.summary_iterator(next(event_paths))
self.assertEqual("brain.Event:2", next(events).file_version)
self.assertEqual("three", next(events).summary.value[0].tag)
self.assertRaises(StopIteration, lambda: next(events))
# Third file should have "four" and "five"
events = summary_iterator.summary_iterator(next(event_paths))
self.assertEqual("brain.Event:2", next(events).file_version)
self.assertEqual("four", next(events).summary.value[0].tag)
self.assertEqual("five", next(events).summary.value[0].tag)
self.assertRaises(StopIteration, lambda: next(events))
# No more files
self.assertRaises(StopIteration, lambda: next(event_paths))
def testSummaryFileWritersInvalidInput(self):
# Test case for GitHub issue 46909
logdir = self.get_temp_dir()
with session.Session() as sess:
with self.assertRaises(errors_impl.InvalidArgumentError):
writer = summary_ops_v2.create_file_writer(
logdir=logdir, flush_millis=[1, 2])
sess.run(writer.init())
sess.run(writer.flush())
class FileWriterCacheTest(test.TestCase):
"""FileWriterCache tests."""
def _test_dir(self, test_name):
"""Create an empty dir to use for tests.
Args:
test_name: Name of the test.
Returns:
Absolute path to the test directory.
"""
test_dir = os.path.join(self.get_temp_dir(), test_name)
if os.path.isdir(test_dir):
for f in glob.glob("%s/*" % test_dir):
os.remove(f)
else:
os.makedirs(test_dir)
return test_dir
def test_cache(self):
with ops.Graph().as_default():
dir1 = self._test_dir("test_cache_1")
dir2 = self._test_dir("test_cache_2")
sw1 = writer_cache.FileWriterCache.get(dir1)
sw2 = writer_cache.FileWriterCache.get(dir2)
sw3 = writer_cache.FileWriterCache.get(dir1)
self.assertEqual(sw1, sw3)
self.assertFalse(sw1 == sw2)
sw1.close()
sw2.close()
events1 = glob.glob(os.path.join(dir1, "event*"))
self.assertTrue(events1)
events2 = glob.glob(os.path.join(dir2, "event*"))
self.assertTrue(events2)
events3 = glob.glob(os.path.join("nowriter", "event*"))
self.assertFalse(events3)
def test_clear(self):
with ops.Graph().as_default():
dir1 = self._test_dir("test_clear")
sw1 = writer_cache.FileWriterCache.get(dir1)
writer_cache.FileWriterCache.clear()
sw2 = writer_cache.FileWriterCache.get(dir1)
self.assertFalse(sw1 == sw2)
if __name__ == "__main__":
test.main()