generate_py_protobufs.py
Go to the documentation of this file.
1 # Protocol Buffers - Google's data interchange format
2 # Copyright 2008 Google Inc. All rights reserved.
3 # https://developers.google.com/protocol-buffers/
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
7 # met:
8 #
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the
14 # distribution.
15 # * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 """Implements the generate_py_protobufs command."""
32 
33 __author__ = 'dlj@google.com (David L. Jones)'
34 
35 import glob
36 import sys
37 import os
38 import distutils.spawn as spawn
39 from distutils.cmd import Command
40 from distutils.errors import DistutilsOptionError, DistutilsExecError
41 
42 class generate_py_protobufs(Command):
43  """Generates Python sources for .proto files."""
44 
45  description = 'Generate Python sources for .proto files'
46  user_options = [
47  ('extra-proto-paths=', None,
48  'Additional paths to resolve imports in .proto files.'),
49 
50  ('protoc=', None,
51  'Path to a specific `protoc` command to use.'),
52  ]
53  boolean_options = ['recurse']
54 
55  def initialize_options(self):
56  """Sets the defaults for the command options."""
57  self.source_dir = None
58  self.proto_root_path = None
60  self.output_dir = '.'
61  self.proto_files = None
62  self.recurse = True
63  self.protoc = None
64 
65  def finalize_options(self):
66  """Sets the final values for the command options.
67 
68  Defaults were set in `initialize_options`, but could have been changed
69  by command-line options or by other commands.
70  """
71  self.ensure_dirname('source_dir')
72  self.ensure_string_list('extra_proto_paths')
73 
74  if self.output_dir is None:
75  self.output_dir = '.'
76  self.ensure_dirname('output_dir')
77 
78  # SUBTLE: if 'source_dir' is a subdirectory of any entry in
79  # 'extra_proto_paths', then in general, the shortest --proto_path prefix
80  # (and the longest relative .proto filenames) must be used for
81  # correctness. For example, consider:
82  #
83  # source_dir = 'a/b/c'
84  # extra_proto_paths = ['a/b', 'x/y']
85  #
86  # In this case, we must ensure that a/b/c/d/foo.proto resolves
87  # canonically as c/d/foo.proto, not just d/foo.proto. Otherwise, this
88  # import:
89  #
90  # import "c/d/foo.proto";
91  #
92  # would result in different FileDescriptor.name keys from "d/foo.proto".
93  # That will cause all the definitions in the file to be flagged as
94  # duplicates, with an error similar to:
95  #
96  # c/d/foo.proto: "packagename.MessageName" is already defined in file "d/foo.proto"
97  #
98  # For paths in self.proto_files, we transform them to be relative to
99  # self.proto_root_path, which may be different from self.source_dir.
100  #
101  # Although the order of --proto_paths is significant, shadowed filenames
102  # are errors: if 'a/b/c.proto' resolves to different files under two
103  # different --proto_path arguments, then the path is rejected as an
104  # error. (Implementation note: this is enforced in protoc's
105  # DiskSourceTree class.)
106 
107  if self.proto_root_path is None:
108  self.proto_root_path = os.path.normpath(self.source_dir)
109  for root_candidate in self.extra_proto_paths:
110  root_candidate = os.path.normpath(root_candidate)
111  if self.proto_root_path.startswith(root_candidate):
112  self.proto_root_path = root_candidate
113  if self.proto_root_path != self.source_dir:
114  self.announce('using computed proto_root_path: ' + self.proto_root_path, level=2)
115 
116  if not self.source_dir.startswith(self.proto_root_path):
117  raise DistutilsOptionError('source_dir ' + self.source_dir +
118  ' is not under proto_root_path ' + self.proto_root_path)
119 
120  if self.proto_files is None:
121  files = glob.glob(os.path.join(self.source_dir, '*.proto'))
122  if self.recurse:
123  files.extend(glob.glob(os.path.join(self.source_dir, '**', '*.proto'), recursive=True))
124  self.proto_files = [f.partition(self.proto_root_path + os.path.sep)[-1] for f in files]
125  if not self.proto_files:
126  raise DistutilsOptionError('no .proto files were found under ' + self.source_dir)
127 
128  self.ensure_string_list('proto_files')
129 
130  if self.protoc is None:
131  self.protoc = os.getenv('PROTOC')
132  if self.protoc is None:
133  self.protoc = spawn.find_executable('protoc')
134 
135  def run(self):
136  # All proto file paths were adjusted in finalize_options to be relative
137  # to self.proto_root_path.
138  proto_paths = ['--proto_path=' + self.proto_root_path]
139  proto_paths.extend(['--proto_path=' + x for x in self.extra_proto_paths])
140 
141  # Run protoc. It was already resolved, so don't try to resolve
142  # through PATH.
143  spawn.spawn(
144  [self.protoc,
145  '--python_out=' + self.output_dir,
146  ] + proto_paths + self.proto_files,
147  search_path=0)
protobuf_distutils.generate_py_protobufs.generate_py_protobufs.protoc
protoc
Definition: generate_py_protobufs.py:63
protobuf_distutils.generate_py_protobufs.generate_py_protobufs.finalize_options
def finalize_options(self)
Definition: generate_py_protobufs.py:65
protobuf_distutils.generate_py_protobufs.generate_py_protobufs.output_dir
output_dir
Definition: generate_py_protobufs.py:60
protobuf_distutils.generate_py_protobufs.generate_py_protobufs.recurse
recurse
Definition: generate_py_protobufs.py:62
protobuf_distutils.generate_py_protobufs.generate_py_protobufs.extra_proto_paths
extra_proto_paths
Definition: generate_py_protobufs.py:59
protobuf_distutils.generate_py_protobufs.generate_py_protobufs.source_dir
source_dir
Definition: generate_py_protobufs.py:57
protobuf_distutils.generate_py_protobufs.generate_py_protobufs.run
def run(self)
Definition: generate_py_protobufs.py:135
protobuf_distutils.generate_py_protobufs.generate_py_protobufs.proto_root_path
proto_root_path
Definition: generate_py_protobufs.py:58
protobuf_distutils.generate_py_protobufs.generate_py_protobufs
Definition: generate_py_protobufs.py:42
protobuf_distutils.generate_py_protobufs.generate_py_protobufs.initialize_options
def initialize_options(self)
Definition: generate_py_protobufs.py:55
protobuf_distutils.generate_py_protobufs.generate_py_protobufs.proto_files
proto_files
Definition: generate_py_protobufs.py:61


grpc
Author(s):
autogenerated on Thu Mar 13 2025 02:59:23