diff --git a/.kokoro/docs/common.cfg b/.kokoro/docs/common.cfg index fcc98d5e..c90da11c 100644 --- a/.kokoro/docs/common.cfg +++ b/.kokoro/docs/common.cfg @@ -30,7 +30,7 @@ env_vars: { env_vars: { key: "V2_STAGING_BUCKET" - value: "docs-staging-v2-staging" + value: "docs-staging-v2" } # It will upload the docker image after successful builds. diff --git a/.kokoro/samples/python3.6/common.cfg b/.kokoro/samples/python3.6/common.cfg index f754d781..a042a54a 100644 --- a/.kokoro/samples/python3.6/common.cfg +++ b/.kokoro/samples/python3.6/common.cfg @@ -13,6 +13,12 @@ env_vars: { value: "py-3.6" } +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py36" +} + env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/python-documentai/.kokoro/test-samples.sh" diff --git a/.kokoro/samples/python3.7/common.cfg b/.kokoro/samples/python3.7/common.cfg index ac8e6e0a..10b51166 100644 --- a/.kokoro/samples/python3.7/common.cfg +++ b/.kokoro/samples/python3.7/common.cfg @@ -13,6 +13,12 @@ env_vars: { value: "py-3.7" } +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py37" +} + env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/python-documentai/.kokoro/test-samples.sh" diff --git a/.kokoro/samples/python3.8/common.cfg b/.kokoro/samples/python3.8/common.cfg index 72a772e8..9a69601d 100644 --- a/.kokoro/samples/python3.8/common.cfg +++ b/.kokoro/samples/python3.8/common.cfg @@ -13,6 +13,12 @@ env_vars: { value: "py-3.8" } +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py38" +} + env_vars: { key: "TRAMPOLINE_BUILD_FILE" value: "github/python-documentai/.kokoro/test-samples.sh" diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh index 557116a9..f97019c6 100755 --- a/.kokoro/test-samples.sh +++ b/.kokoro/test-samples.sh @@ -28,6 +28,12 @@ if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then git checkout $LATEST_RELEASE fi +# Exit early if samples directory doesn't exist +if [ ! -d "./samples" ]; then + echo "No tests run. `./samples` not found" + exit 0 +fi + # Disable buffering, so that the logs stream through. export PYTHONUNBUFFERED=1 @@ -101,4 +107,4 @@ cd "$ROOT" # Workaround for Kokoro permissions issue: delete secrets rm testing/{test-env.sh,client-secrets.json,service-account.json} -exit "$RTN" \ No newline at end of file +exit "$RTN" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index b3d1f602..039f4368 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,44 +1,95 @@ -# Contributor Code of Conduct +# Code of Conduct -As contributors and maintainers of this project, -and in the interest of fostering an open and welcoming community, -we pledge to respect all people who contribute through reporting issues, -posting feature requests, updating documentation, -submitting pull requests or patches, and other activities. +## Our Pledge -We are committed to making participation in this project -a harassment-free experience for everyone, -regardless of level of experience, gender, gender identity and expression, -sexual orientation, disability, personal appearance, -body size, race, ethnicity, age, religion, or nationality. +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of +experience, education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery -* Personal attacks -* Trolling or insulting/derogatory comments -* Public or private harassment -* Publishing other's private information, -such as physical or electronic -addresses, without explicit permission -* Other unethical or unprofessional conduct. +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct. -By adopting this Code of Conduct, -project maintainers commit themselves to fairly and consistently -applying these principles to every aspect of managing this project. -Project maintainers who do not follow or enforce the Code of Conduct -may be permanently removed from the project team. - -This code of conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. - -Instances of abusive, harassing, or otherwise unacceptable behavior -may be reported by opening an issue -or contacting one or more of the project maintainers. - -This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, -available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +This Code of Conduct also applies outside the project spaces when the Project +Steward has a reasonable belief that an individual's behavior may have a +negative impact on the project or its community. + +## Conflict Resolution + +We do not believe that all conflict is bad; healthy debate and disagreement +often yield positive results. However, it is never okay to be disrespectful or +to engage in behavior that violates the project’s code of conduct. + +If you see someone violating the code of conduct, you are encouraged to address +the behavior directly with those involved. Many issues can be resolved quickly +and easily, and this gives people more control over the outcome of their +dispute. If you are unable to resolve the matter for any reason, or if the +behavior is threatening or harassing, report it. We are dedicated to providing +an environment where participants feel welcome and safe. + + +Reports should be directed to *googleapis-stewards@google.com*, the +Project Steward(s) for *Google Cloud Client Libraries*. It is the Project Steward’s duty to +receive and address reported violations of the code of conduct. They will then +work with a committee consisting of representatives from the Open Source +Programs Office and the Google Open Source Strategy team. If for any reason you +are uncomfortable reaching out to the Project Steward, please email +opensource@google.com. + +We will investigate every complaint, but you may not receive a direct response. +We will use our discretion in determining when and how to follow up on reported +incidents, which may range from not taking action to permanent expulsion from +the project and project-sponsored spaces. We will notify the accused of the +report and provide them an opportunity to discuss it before any action is taken. +The identity of the reporter will be omitted from the details of the report +supplied to the accused. In potentially harmful situations, such as ongoing +harassment or threats to anyone's safety, we may take action without notice. + +## Attribution + +This Code of Conduct is adapted from the Contributor Covenant, version 1.4, +available at +https://www.contributor-covenant.org/version/1/4/code-of-conduct.html \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index d5ee4abd..6d80d2bf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -349,6 +349,7 @@ "google-auth": ("https://google-auth.readthedocs.io/en/stable", None), "google.api_core": ("https://googleapis.dev/python/google-api-core/latest/", None,), "grpc": ("https://grpc.io/grpc/python/", None), + "proto-plus": ("https://proto-plus-python.readthedocs.io/en/latest/", None), } diff --git a/docs/documentai_v1beta2/types.rst b/docs/documentai_v1beta2/types.rst index 2a437e9d..35540dd0 100644 --- a/docs/documentai_v1beta2/types.rst +++ b/docs/documentai_v1beta2/types.rst @@ -3,3 +3,4 @@ Types for Google Cloud Documentai v1beta2 API .. automodule:: google.cloud.documentai_v1beta2.types :members: + :show-inheritance: diff --git a/docs/documentai_v1beta3/types.rst b/docs/documentai_v1beta3/types.rst index 03bcbfa7..31b489da 100644 --- a/docs/documentai_v1beta3/types.rst +++ b/docs/documentai_v1beta3/types.rst @@ -3,3 +3,4 @@ Types for Google Cloud Documentai v1beta3 API .. automodule:: google.cloud.documentai_v1beta3.types :members: + :show-inheritance: diff --git a/google/cloud/documentai_v1beta2/services/document_understanding_service/async_client.py b/google/cloud/documentai_v1beta2/services/document_understanding_service/async_client.py index a293e4b2..c961662b 100644 --- a/google/cloud/documentai_v1beta2/services/document_understanding_service/async_client.py +++ b/google/cloud/documentai_v1beta2/services/document_understanding_service/async_client.py @@ -50,11 +50,55 @@ class DocumentUnderstandingServiceAsyncClient: DEFAULT_ENDPOINT = DocumentUnderstandingServiceClient.DEFAULT_ENDPOINT DEFAULT_MTLS_ENDPOINT = DocumentUnderstandingServiceClient.DEFAULT_MTLS_ENDPOINT + common_billing_account_path = staticmethod( + DocumentUnderstandingServiceClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + DocumentUnderstandingServiceClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod( + DocumentUnderstandingServiceClient.common_folder_path + ) + parse_common_folder_path = staticmethod( + DocumentUnderstandingServiceClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + DocumentUnderstandingServiceClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + DocumentUnderstandingServiceClient.parse_common_organization_path + ) + + common_project_path = staticmethod( + DocumentUnderstandingServiceClient.common_project_path + ) + parse_common_project_path = staticmethod( + DocumentUnderstandingServiceClient.parse_common_project_path + ) + + common_location_path = staticmethod( + DocumentUnderstandingServiceClient.common_location_path + ) + parse_common_location_path = staticmethod( + DocumentUnderstandingServiceClient.parse_common_location_path + ) + from_service_account_file = ( DocumentUnderstandingServiceClient.from_service_account_file ) from_service_account_json = from_service_account_file + @property + def transport(self) -> DocumentUnderstandingServiceTransport: + """Return the transport used by the client instance. + + Returns: + DocumentUnderstandingServiceTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(DocumentUnderstandingServiceClient).get_transport_class, type(DocumentUnderstandingServiceClient), @@ -152,7 +196,8 @@ async def batch_process_documents( # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - if request is not None and any([requests]): + has_flattened_params = any([requests]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -163,8 +208,8 @@ async def batch_process_documents( # If we have keyword arguments corresponding to fields on the # request, apply these. - if requests is not None: - request.requests = requests + if requests: + request.requests.extend(requests) # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. @@ -175,7 +220,7 @@ async def batch_process_documents( maximum=60.0, multiplier=1.3, predicate=retries.if_exception_type( - exceptions.ServiceUnavailable, exceptions.DeadlineExceeded, + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), ), default_timeout=120.0, @@ -246,7 +291,7 @@ async def process_document( maximum=60.0, multiplier=1.3, predicate=retries.if_exception_type( - exceptions.ServiceUnavailable, exceptions.DeadlineExceeded, + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), ), default_timeout=120.0, diff --git a/google/cloud/documentai_v1beta2/services/document_understanding_service/client.py b/google/cloud/documentai_v1beta2/services/document_understanding_service/client.py index 433de5c1..0d740e5b 100644 --- a/google/cloud/documentai_v1beta2/services/document_understanding_service/client.py +++ b/google/cloud/documentai_v1beta2/services/document_understanding_service/client.py @@ -142,6 +142,74 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> DocumentUnderstandingServiceTransport: + """Return the transport used by the client instance. + + Returns: + DocumentUnderstandingServiceTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def common_billing_account_path(billing_account: str,) -> str: + """Return a fully-qualified billing_account string.""" + return "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + + @staticmethod + def parse_common_billing_account_path(path: str) -> Dict[str, str]: + """Parse a billing_account path into its component segments.""" + m = re.match(r"^billingAccounts/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_folder_path(folder: str,) -> str: + """Return a fully-qualified folder string.""" + return "folders/{folder}".format(folder=folder,) + + @staticmethod + def parse_common_folder_path(path: str) -> Dict[str, str]: + """Parse a folder path into its component segments.""" + m = re.match(r"^folders/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_organization_path(organization: str,) -> str: + """Return a fully-qualified organization string.""" + return "organizations/{organization}".format(organization=organization,) + + @staticmethod + def parse_common_organization_path(path: str) -> Dict[str, str]: + """Parse a organization path into its component segments.""" + m = re.match(r"^organizations/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_project_path(project: str,) -> str: + """Return a fully-qualified project string.""" + return "projects/{project}".format(project=project,) + + @staticmethod + def parse_common_project_path(path: str) -> Dict[str, str]: + """Parse a project path into its component segments.""" + m = re.match(r"^projects/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_location_path(project: str, location: str,) -> str: + """Return a fully-qualified location string.""" + return "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + + @staticmethod + def parse_common_location_path(path: str) -> Dict[str, str]: + """Parse a location path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) + return m.groupdict() if m else {} + def __init__( self, *, @@ -177,10 +245,10 @@ def __init__( not provided, the default SSL client certificate will be used if present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not set, no client certificate will be used. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -320,8 +388,8 @@ def batch_process_documents( # If we have keyword arguments corresponding to fields on the # request, apply these. - if requests is not None: - request.requests = requests + if requests: + request.requests.extend(requests) # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. diff --git a/google/cloud/documentai_v1beta2/services/document_understanding_service/transports/base.py b/google/cloud/documentai_v1beta2/services/document_understanding_service/transports/base.py index df52dbcc..547c5803 100644 --- a/google/cloud/documentai_v1beta2/services/document_understanding_service/transports/base.py +++ b/google/cloud/documentai_v1beta2/services/document_understanding_service/transports/base.py @@ -116,7 +116,7 @@ def _prep_wrapped_messages(self, client_info): maximum=60.0, multiplier=1.3, predicate=retries.if_exception_type( - exceptions.ServiceUnavailable, exceptions.DeadlineExceeded, + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), ), default_timeout=120.0, @@ -129,7 +129,7 @@ def _prep_wrapped_messages(self, client_info): maximum=60.0, multiplier=1.3, predicate=retries.if_exception_type( - exceptions.ServiceUnavailable, exceptions.DeadlineExceeded, + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), ), default_timeout=120.0, diff --git a/google/cloud/documentai_v1beta2/services/document_understanding_service/transports/grpc.py b/google/cloud/documentai_v1beta2/services/document_understanding_service/transports/grpc.py index 60f3e8b8..0390ff51 100644 --- a/google/cloud/documentai_v1beta2/services/document_understanding_service/transports/grpc.py +++ b/google/cloud/documentai_v1beta2/services/document_understanding_service/transports/grpc.py @@ -94,10 +94,10 @@ def __init__( for grpc channel. It is ignored if ``channel`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -106,6 +106,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -113,6 +115,7 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: warnings.warn( "api_mtls_endpoint and client_cert_source are deprecated", @@ -149,6 +152,7 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + self._ssl_channel_credentials = ssl_credentials else: host = host if ":" in host else host + ":443" @@ -226,12 +230,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.Channel: - """Create the channel designed to connect to this service. - - This property caches on the instance; repeated calls return - the same channel. + """Return the channel designed to connect to this service. """ - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/documentai_v1beta2/services/document_understanding_service/transports/grpc_asyncio.py b/google/cloud/documentai_v1beta2/services/document_understanding_service/transports/grpc_asyncio.py index 315795e5..95122515 100644 --- a/google/cloud/documentai_v1beta2/services/document_understanding_service/transports/grpc_asyncio.py +++ b/google/cloud/documentai_v1beta2/services/document_understanding_service/transports/grpc_asyncio.py @@ -153,6 +153,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -160,6 +162,7 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: warnings.warn( "api_mtls_endpoint and client_cert_source are deprecated", @@ -196,6 +199,7 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + self._ssl_channel_credentials = ssl_credentials else: host = host if ":" in host else host + ":443" diff --git a/google/cloud/documentai_v1beta2/types/geometry.py b/google/cloud/documentai_v1beta2/types/geometry.py index 12d63f90..0592f336 100644 --- a/google/cloud/documentai_v1beta2/types/geometry.py +++ b/google/cloud/documentai_v1beta2/types/geometry.py @@ -68,10 +68,10 @@ class BoundingPoly(proto.Message): The bounding polygon normalized vertices. """ - vertices = proto.RepeatedField(proto.MESSAGE, number=1, message=Vertex,) + vertices = proto.RepeatedField(proto.MESSAGE, number=1, message="Vertex",) normalized_vertices = proto.RepeatedField( - proto.MESSAGE, number=2, message=NormalizedVertex, + proto.MESSAGE, number=2, message="NormalizedVertex", ) diff --git a/google/cloud/documentai_v1beta3/services/document_processor_service/async_client.py b/google/cloud/documentai_v1beta3/services/document_processor_service/async_client.py index 7ba80ac2..467b072a 100644 --- a/google/cloud/documentai_v1beta3/services/document_processor_service/async_client.py +++ b/google/cloud/documentai_v1beta3/services/document_processor_service/async_client.py @@ -51,9 +51,62 @@ class DocumentProcessorServiceAsyncClient: DEFAULT_ENDPOINT = DocumentProcessorServiceClient.DEFAULT_ENDPOINT DEFAULT_MTLS_ENDPOINT = DocumentProcessorServiceClient.DEFAULT_MTLS_ENDPOINT + human_review_config_path = staticmethod( + DocumentProcessorServiceClient.human_review_config_path + ) + parse_human_review_config_path = staticmethod( + DocumentProcessorServiceClient.parse_human_review_config_path + ) + processor_path = staticmethod(DocumentProcessorServiceClient.processor_path) + parse_processor_path = staticmethod( + DocumentProcessorServiceClient.parse_processor_path + ) + + common_billing_account_path = staticmethod( + DocumentProcessorServiceClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + DocumentProcessorServiceClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(DocumentProcessorServiceClient.common_folder_path) + parse_common_folder_path = staticmethod( + DocumentProcessorServiceClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + DocumentProcessorServiceClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + DocumentProcessorServiceClient.parse_common_organization_path + ) + + common_project_path = staticmethod( + DocumentProcessorServiceClient.common_project_path + ) + parse_common_project_path = staticmethod( + DocumentProcessorServiceClient.parse_common_project_path + ) + + common_location_path = staticmethod( + DocumentProcessorServiceClient.common_location_path + ) + parse_common_location_path = staticmethod( + DocumentProcessorServiceClient.parse_common_location_path + ) + from_service_account_file = DocumentProcessorServiceClient.from_service_account_file from_service_account_json = from_service_account_file + @property + def transport(self) -> DocumentProcessorServiceTransport: + """Return the transport used by the client instance. + + Returns: + DocumentProcessorServiceTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(DocumentProcessorServiceClient).get_transport_class, type(DocumentProcessorServiceClient), @@ -144,7 +197,8 @@ async def process_document( # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - if request is not None and any([name]): + has_flattened_params = any([name]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -167,7 +221,7 @@ async def process_document( maximum=60.0, multiplier=1.3, predicate=retries.if_exception_type( - exceptions.ServiceUnavailable, exceptions.DeadlineExceeded, + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), ), default_timeout=120.0, @@ -227,7 +281,8 @@ async def batch_process_documents( # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - if request is not None and any([name]): + has_flattened_params = any([name]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -250,7 +305,7 @@ async def batch_process_documents( maximum=60.0, multiplier=1.3, predicate=retries.if_exception_type( - exceptions.ServiceUnavailable, exceptions.DeadlineExceeded, + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), ), default_timeout=120.0, @@ -319,7 +374,8 @@ async def review_document( # Create or coerce a protobuf request object. # Sanity check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - if request is not None and any([human_review_config]): + has_flattened_params = any([human_review_config]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -342,7 +398,7 @@ async def review_document( maximum=60.0, multiplier=1.3, predicate=retries.if_exception_type( - exceptions.ServiceUnavailable, exceptions.DeadlineExceeded, + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), ), default_timeout=120.0, diff --git a/google/cloud/documentai_v1beta3/services/document_processor_service/client.py b/google/cloud/documentai_v1beta3/services/document_processor_service/client.py index 84b57f36..e621e251 100644 --- a/google/cloud/documentai_v1beta3/services/document_processor_service/client.py +++ b/google/cloud/documentai_v1beta3/services/document_processor_service/client.py @@ -139,6 +139,106 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> DocumentProcessorServiceTransport: + """Return the transport used by the client instance. + + Returns: + DocumentProcessorServiceTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def human_review_config_path(project: str, location: str, processor: str,) -> str: + """Return a fully-qualified human_review_config string.""" + return "projects/{project}/locations/{location}/processors/{processor}/humanReviewConfig".format( + project=project, location=location, processor=processor, + ) + + @staticmethod + def parse_human_review_config_path(path: str) -> Dict[str, str]: + """Parse a human_review_config path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/processors/(?P.+?)/humanReviewConfig$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def processor_path(project: str, location: str, processor: str,) -> str: + """Return a fully-qualified processor string.""" + return "projects/{project}/locations/{location}/processors/{processor}".format( + project=project, location=location, processor=processor, + ) + + @staticmethod + def parse_processor_path(path: str) -> Dict[str, str]: + """Parse a processor path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/processors/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def common_billing_account_path(billing_account: str,) -> str: + """Return a fully-qualified billing_account string.""" + return "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + + @staticmethod + def parse_common_billing_account_path(path: str) -> Dict[str, str]: + """Parse a billing_account path into its component segments.""" + m = re.match(r"^billingAccounts/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_folder_path(folder: str,) -> str: + """Return a fully-qualified folder string.""" + return "folders/{folder}".format(folder=folder,) + + @staticmethod + def parse_common_folder_path(path: str) -> Dict[str, str]: + """Parse a folder path into its component segments.""" + m = re.match(r"^folders/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_organization_path(organization: str,) -> str: + """Return a fully-qualified organization string.""" + return "organizations/{organization}".format(organization=organization,) + + @staticmethod + def parse_common_organization_path(path: str) -> Dict[str, str]: + """Parse a organization path into its component segments.""" + m = re.match(r"^organizations/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_project_path(project: str,) -> str: + """Return a fully-qualified project string.""" + return "projects/{project}".format(project=project,) + + @staticmethod + def parse_common_project_path(path: str) -> Dict[str, str]: + """Parse a project path into its component segments.""" + m = re.match(r"^projects/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_location_path(project: str, location: str,) -> str: + """Return a fully-qualified location string.""" + return "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + + @staticmethod + def parse_common_location_path(path: str) -> Dict[str, str]: + """Parse a location path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/locations/(?P.+?)$", path) + return m.groupdict() if m else {} + def __init__( self, *, @@ -174,10 +274,10 @@ def __init__( not provided, the default SSL client certificate will be used if present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not set, no client certificate will be used. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: diff --git a/google/cloud/documentai_v1beta3/services/document_processor_service/transports/base.py b/google/cloud/documentai_v1beta3/services/document_processor_service/transports/base.py index dfa32e24..e24d4922 100644 --- a/google/cloud/documentai_v1beta3/services/document_processor_service/transports/base.py +++ b/google/cloud/documentai_v1beta3/services/document_processor_service/transports/base.py @@ -115,7 +115,7 @@ def _prep_wrapped_messages(self, client_info): maximum=60.0, multiplier=1.3, predicate=retries.if_exception_type( - exceptions.ServiceUnavailable, exceptions.DeadlineExceeded, + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), ), default_timeout=120.0, @@ -128,7 +128,7 @@ def _prep_wrapped_messages(self, client_info): maximum=60.0, multiplier=1.3, predicate=retries.if_exception_type( - exceptions.ServiceUnavailable, exceptions.DeadlineExceeded, + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), ), default_timeout=120.0, @@ -141,7 +141,7 @@ def _prep_wrapped_messages(self, client_info): maximum=60.0, multiplier=1.3, predicate=retries.if_exception_type( - exceptions.ServiceUnavailable, exceptions.DeadlineExceeded, + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, ), ), default_timeout=120.0, diff --git a/google/cloud/documentai_v1beta3/services/document_processor_service/transports/grpc.py b/google/cloud/documentai_v1beta3/services/document_processor_service/transports/grpc.py index d7220126..435767b0 100644 --- a/google/cloud/documentai_v1beta3/services/document_processor_service/transports/grpc.py +++ b/google/cloud/documentai_v1beta3/services/document_processor_service/transports/grpc.py @@ -95,10 +95,10 @@ def __init__( for grpc channel. It is ignored if ``channel`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -107,6 +107,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -114,6 +116,7 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: warnings.warn( "api_mtls_endpoint and client_cert_source are deprecated", @@ -150,6 +153,7 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + self._ssl_channel_credentials = ssl_credentials else: host = host if ":" in host else host + ":443" @@ -227,12 +231,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.Channel: - """Create the channel designed to connect to this service. - - This property caches on the instance; repeated calls return - the same channel. + """Return the channel designed to connect to this service. """ - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/documentai_v1beta3/services/document_processor_service/transports/grpc_asyncio.py b/google/cloud/documentai_v1beta3/services/document_processor_service/transports/grpc_asyncio.py index 391819cf..df9be8a8 100644 --- a/google/cloud/documentai_v1beta3/services/document_processor_service/transports/grpc_asyncio.py +++ b/google/cloud/documentai_v1beta3/services/document_processor_service/transports/grpc_asyncio.py @@ -152,6 +152,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -159,6 +161,7 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: warnings.warn( "api_mtls_endpoint and client_cert_source are deprecated", @@ -195,6 +198,7 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + self._ssl_channel_credentials = ssl_credentials else: host = host if ":" in host else host + ":443" diff --git a/google/cloud/documentai_v1beta3/types/geometry.py b/google/cloud/documentai_v1beta3/types/geometry.py index e87b87c7..b72dab75 100644 --- a/google/cloud/documentai_v1beta3/types/geometry.py +++ b/google/cloud/documentai_v1beta3/types/geometry.py @@ -68,10 +68,10 @@ class BoundingPoly(proto.Message): The bounding polygon normalized vertices. """ - vertices = proto.RepeatedField(proto.MESSAGE, number=1, message=Vertex,) + vertices = proto.RepeatedField(proto.MESSAGE, number=1, message="Vertex",) normalized_vertices = proto.RepeatedField( - proto.MESSAGE, number=2, message=NormalizedVertex, + proto.MESSAGE, number=2, message="NormalizedVertex", ) diff --git a/noxfile.py b/noxfile.py index e446dd8d..d531ac69 100644 --- a/noxfile.py +++ b/noxfile.py @@ -28,7 +28,7 @@ DEFAULT_PYTHON_VERSION = "3.8" SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"] -UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8"] +UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9"] @nox.session(python=DEFAULT_PYTHON_VERSION) @@ -72,7 +72,9 @@ def default(session): # Install all test dependencies, then install this package in-place. session.install("asyncmock", "pytest-asyncio") - session.install("mock", "pytest", "pytest-cov") + session.install( + "mock", "pytest", "pytest-cov", + ) session.install("-e", ".") # Run py.test against the unit tests. diff --git a/scripts/fixup_documentai_v1beta2_keywords.py b/scripts/fixup_documentai_v1beta2_keywords.py index 0cb9fcbf..d2f24146 100644 --- a/scripts/fixup_documentai_v1beta2_keywords.py +++ b/scripts/fixup_documentai_v1beta2_keywords.py @@ -1,3 +1,4 @@ +#! /usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2020 Google LLC diff --git a/scripts/fixup_documentai_v1beta3_keywords.py b/scripts/fixup_documentai_v1beta3_keywords.py index 2b689522..750630f1 100644 --- a/scripts/fixup_documentai_v1beta3_keywords.py +++ b/scripts/fixup_documentai_v1beta3_keywords.py @@ -1,3 +1,4 @@ +#! /usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2020 Google LLC diff --git a/synth.metadata b/synth.metadata index 3a7962ec..8088f01c 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,21 +4,29 @@ "git": { "name": ".", "remote": "git@github.com:googleapis/python-documentai", - "sha": "ec70a8cec0f1fbd0f8ec18189139e632ec28b025" + "sha": "c6186ea7a58f83bc2e49d9df2a48fce3f78f0143" + } + }, + { + "git": { + "name": "googleapis", + "remote": "https://github.com/googleapis/googleapis.git", + "sha": "e3e7e7ddb0fecd7bc61ca03b5a9ddb29cc9b48d8", + "internalRef": "342967619" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "e6168630be3e31eede633ba2c6f1cd64248dec1c" + "sha": "7fcc405a579d5d53a726ff3da1b7c8c08f0f2d58" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "e6168630be3e31eede633ba2c6f1cd64248dec1c" + "sha": "7fcc405a579d5d53a726ff3da1b7c8c08f0f2d58" } } ], diff --git a/synth.py b/synth.py index 3ae8445c..3281f52f 100644 --- a/synth.py +++ b/synth.py @@ -54,6 +54,6 @@ excludes=[".coveragerc"], # microgenerator has a good .coveragerc file ) -python.py_samples() +python.py_samples(skip_readmes=True) s.shell.run(["nox", "-s", "blacken"], hide_output=False) diff --git a/tests/unit/gapic/documentai_v1beta2/test_document_understanding_service.py b/tests/unit/gapic/documentai_v1beta2/test_document_understanding_service.py index 290cb2f2..17fdb324 100644 --- a/tests/unit/gapic/documentai_v1beta2/test_document_understanding_service.py +++ b/tests/unit/gapic/documentai_v1beta2/test_document_understanding_service.py @@ -110,12 +110,12 @@ def test_document_understanding_service_client_from_service_account_file(client_ ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds - assert client._transport._host == "us-documentai.googleapis.com:443" + assert client.transport._host == "us-documentai.googleapis.com:443" def test_document_understanding_service_client_get_transport_class(): @@ -493,7 +493,7 @@ def test_batch_process_documents( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.batch_process_documents), "__call__" + type(client.transport.batch_process_documents), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/spam") @@ -515,18 +515,21 @@ def test_batch_process_documents_from_dict(): @pytest.mark.asyncio -async def test_batch_process_documents_async(transport: str = "grpc_asyncio"): +async def test_batch_process_documents_async( + transport: str = "grpc_asyncio", + request_type=document_understanding.BatchProcessDocumentsRequest, +): client = DocumentUnderstandingServiceAsyncClient( credentials=credentials.AnonymousCredentials(), transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, # and we are mocking out the actual API, so just send an empty request. - request = document_understanding.BatchProcessDocumentsRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.batch_process_documents), "__call__" + type(client.transport.batch_process_documents), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -539,12 +542,17 @@ async def test_batch_process_documents_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == document_understanding.BatchProcessDocumentsRequest() # Establish that the response is the type that we expect. assert isinstance(response, future.Future) +@pytest.mark.asyncio +async def test_batch_process_documents_async_from_dict(): + await test_batch_process_documents_async(request_type=dict) + + def test_batch_process_documents_field_headers(): client = DocumentUnderstandingServiceClient( credentials=credentials.AnonymousCredentials(), @@ -557,7 +565,7 @@ def test_batch_process_documents_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.batch_process_documents), "__call__" + type(client.transport.batch_process_documents), "__call__" ) as call: call.return_value = operations_pb2.Operation(name="operations/op") @@ -586,7 +594,7 @@ async def test_batch_process_documents_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.batch_process_documents), "__call__" + type(client.transport.batch_process_documents), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/op") @@ -611,7 +619,7 @@ def test_batch_process_documents_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.batch_process_documents), "__call__" + type(client.transport.batch_process_documents), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -658,7 +666,7 @@ async def test_batch_process_documents_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.batch_process_documents), "__call__" + type(client.transport.batch_process_documents), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -713,9 +721,7 @@ def test_process_document( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._transport.process_document), "__call__" - ) as call: + with mock.patch.object(type(client.transport.process_document), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = document.Document( mime_type="mime_type_value", text="text_value", uri="uri_value", @@ -730,6 +736,7 @@ def test_process_document( assert args[0] == document_understanding.ProcessDocumentRequest() # Establish that the response is the type that we expect. + assert isinstance(response, document.Document) assert response.mime_type == "mime_type_value" @@ -742,19 +749,20 @@ def test_process_document_from_dict(): @pytest.mark.asyncio -async def test_process_document_async(transport: str = "grpc_asyncio"): +async def test_process_document_async( + transport: str = "grpc_asyncio", + request_type=document_understanding.ProcessDocumentRequest, +): client = DocumentUnderstandingServiceAsyncClient( credentials=credentials.AnonymousCredentials(), transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, # and we are mocking out the actual API, so just send an empty request. - request = document_understanding.ProcessDocumentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.process_document), "__call__" - ) as call: + with mock.patch.object(type(client.transport.process_document), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( document.Document(mime_type="mime_type_value", text="text_value",) @@ -766,7 +774,7 @@ async def test_process_document_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == document_understanding.ProcessDocumentRequest() # Establish that the response is the type that we expect. assert isinstance(response, document.Document) @@ -776,6 +784,11 @@ async def test_process_document_async(transport: str = "grpc_asyncio"): assert response.text == "text_value" +@pytest.mark.asyncio +async def test_process_document_async_from_dict(): + await test_process_document_async(request_type=dict) + + def test_process_document_field_headers(): client = DocumentUnderstandingServiceClient( credentials=credentials.AnonymousCredentials(), @@ -787,9 +800,7 @@ def test_process_document_field_headers(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._transport.process_document), "__call__" - ) as call: + with mock.patch.object(type(client.transport.process_document), "__call__") as call: call.return_value = document.Document() client.process_document(request) @@ -816,9 +827,7 @@ async def test_process_document_field_headers_async(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.process_document), "__call__" - ) as call: + with mock.patch.object(type(client.transport.process_document), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(document.Document()) await client.process_document(request) @@ -869,7 +878,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = DocumentUnderstandingServiceClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -908,7 +917,7 @@ def test_transport_grpc_default(): credentials=credentials.AnonymousCredentials(), ) assert isinstance( - client._transport, transports.DocumentUnderstandingServiceGrpcTransport, + client.transport, transports.DocumentUnderstandingServiceGrpcTransport, ) @@ -1009,7 +1018,7 @@ def test_document_understanding_service_host_no_port(): api_endpoint="us-documentai.googleapis.com" ), ) - assert client._transport._host == "us-documentai.googleapis.com:443" + assert client.transport._host == "us-documentai.googleapis.com:443" def test_document_understanding_service_host_with_port(): @@ -1019,7 +1028,7 @@ def test_document_understanding_service_host_with_port(): api_endpoint="us-documentai.googleapis.com:8000" ), ) - assert client._transport._host == "us-documentai.googleapis.com:8000" + assert client.transport._host == "us-documentai.googleapis.com:8000" def test_document_understanding_service_grpc_transport_channel(): @@ -1031,6 +1040,7 @@ def test_document_understanding_service_grpc_transport_channel(): ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None def test_document_understanding_service_grpc_asyncio_transport_channel(): @@ -1042,6 +1052,7 @@ def test_document_understanding_service_grpc_asyncio_transport_channel(): ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None @pytest.mark.parametrize( @@ -1089,6 +1100,7 @@ def test_document_understanding_service_transport_channel_mtls_with_client_cert_ quota_project_id=None, ) assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred @pytest.mark.parametrize( @@ -1137,7 +1149,7 @@ def test_document_understanding_service_grpc_lro_client(): client = DocumentUnderstandingServiceClient( credentials=credentials.AnonymousCredentials(), transport="grpc", ) - transport = client._transport + transport = client.transport # Ensure that we have a api-core operations client. assert isinstance(transport.operations_client, operations_v1.OperationsClient,) @@ -1150,7 +1162,7 @@ def test_document_understanding_service_grpc_lro_async_client(): client = DocumentUnderstandingServiceAsyncClient( credentials=credentials.AnonymousCredentials(), transport="grpc_asyncio", ) - transport = client._client._transport + transport = client.transport # Ensure that we have a api-core operations client. assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) @@ -1159,6 +1171,109 @@ def test_document_understanding_service_grpc_lro_async_client(): assert transport.operations_client is transport.operations_client +def test_common_billing_account_path(): + billing_account = "squid" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = DocumentUnderstandingServiceClient.common_billing_account_path( + billing_account + ) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "clam", + } + path = DocumentUnderstandingServiceClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentUnderstandingServiceClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "whelk" + + expected = "folders/{folder}".format(folder=folder,) + actual = DocumentUnderstandingServiceClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "octopus", + } + path = DocumentUnderstandingServiceClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentUnderstandingServiceClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "oyster" + + expected = "organizations/{organization}".format(organization=organization,) + actual = DocumentUnderstandingServiceClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nudibranch", + } + path = DocumentUnderstandingServiceClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentUnderstandingServiceClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "cuttlefish" + + expected = "projects/{project}".format(project=project,) + actual = DocumentUnderstandingServiceClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "mussel", + } + path = DocumentUnderstandingServiceClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentUnderstandingServiceClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "winkle" + location = "nautilus" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = DocumentUnderstandingServiceClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "scallop", + "location": "abalone", + } + path = DocumentUnderstandingServiceClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentUnderstandingServiceClient.parse_common_location_path(path) + assert expected == actual + + def test_client_withDEFAULT_CLIENT_INFO(): client_info = gapic_v1.client_info.ClientInfo() diff --git a/tests/unit/gapic/documentai_v1beta3/test_document_processor_service.py b/tests/unit/gapic/documentai_v1beta3/test_document_processor_service.py index 4b17a5ed..ad4346e8 100644 --- a/tests/unit/gapic/documentai_v1beta3/test_document_processor_service.py +++ b/tests/unit/gapic/documentai_v1beta3/test_document_processor_service.py @@ -117,12 +117,12 @@ def test_document_processor_service_client_from_service_account_file(client_clas ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds - assert client._transport._host == "us-documentai.googleapis.com:443" + assert client.transport._host == "us-documentai.googleapis.com:443" def test_document_processor_service_client_get_transport_class(): @@ -498,9 +498,7 @@ def test_process_document( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._transport.process_document), "__call__" - ) as call: + with mock.patch.object(type(client.transport.process_document), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = document_processor_service.ProcessResponse( human_review_operation="human_review_operation_value", @@ -515,6 +513,7 @@ def test_process_document( assert args[0] == document_processor_service.ProcessRequest() # Establish that the response is the type that we expect. + assert isinstance(response, document_processor_service.ProcessResponse) assert response.human_review_operation == "human_review_operation_value" @@ -525,19 +524,20 @@ def test_process_document_from_dict(): @pytest.mark.asyncio -async def test_process_document_async(transport: str = "grpc_asyncio"): +async def test_process_document_async( + transport: str = "grpc_asyncio", + request_type=document_processor_service.ProcessRequest, +): client = DocumentProcessorServiceAsyncClient( credentials=credentials.AnonymousCredentials(), transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, # and we are mocking out the actual API, so just send an empty request. - request = document_processor_service.ProcessRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.process_document), "__call__" - ) as call: + with mock.patch.object(type(client.transport.process_document), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( document_processor_service.ProcessResponse( @@ -551,7 +551,7 @@ async def test_process_document_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == document_processor_service.ProcessRequest() # Establish that the response is the type that we expect. assert isinstance(response, document_processor_service.ProcessResponse) @@ -559,6 +559,11 @@ async def test_process_document_async(transport: str = "grpc_asyncio"): assert response.human_review_operation == "human_review_operation_value" +@pytest.mark.asyncio +async def test_process_document_async_from_dict(): + await test_process_document_async(request_type=dict) + + def test_process_document_field_headers(): client = DocumentProcessorServiceClient( credentials=credentials.AnonymousCredentials(), @@ -570,9 +575,7 @@ def test_process_document_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._transport.process_document), "__call__" - ) as call: + with mock.patch.object(type(client.transport.process_document), "__call__") as call: call.return_value = document_processor_service.ProcessResponse() client.process_document(request) @@ -599,9 +602,7 @@ async def test_process_document_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.process_document), "__call__" - ) as call: + with mock.patch.object(type(client.transport.process_document), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( document_processor_service.ProcessResponse() ) @@ -624,9 +625,7 @@ def test_process_document_flattened(): ) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._transport.process_document), "__call__" - ) as call: + with mock.patch.object(type(client.transport.process_document), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = document_processor_service.ProcessResponse() @@ -662,9 +661,7 @@ async def test_process_document_flattened_async(): ) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.process_document), "__call__" - ) as call: + with mock.patch.object(type(client.transport.process_document), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = document_processor_service.ProcessResponse() @@ -710,7 +707,7 @@ def test_batch_process_documents( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.batch_process_documents), "__call__" + type(client.transport.batch_process_documents), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/spam") @@ -732,18 +729,21 @@ def test_batch_process_documents_from_dict(): @pytest.mark.asyncio -async def test_batch_process_documents_async(transport: str = "grpc_asyncio"): +async def test_batch_process_documents_async( + transport: str = "grpc_asyncio", + request_type=document_processor_service.BatchProcessRequest, +): client = DocumentProcessorServiceAsyncClient( credentials=credentials.AnonymousCredentials(), transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, # and we are mocking out the actual API, so just send an empty request. - request = document_processor_service.BatchProcessRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.batch_process_documents), "__call__" + type(client.transport.batch_process_documents), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -756,12 +756,17 @@ async def test_batch_process_documents_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == document_processor_service.BatchProcessRequest() # Establish that the response is the type that we expect. assert isinstance(response, future.Future) +@pytest.mark.asyncio +async def test_batch_process_documents_async_from_dict(): + await test_batch_process_documents_async(request_type=dict) + + def test_batch_process_documents_field_headers(): client = DocumentProcessorServiceClient( credentials=credentials.AnonymousCredentials(), @@ -774,7 +779,7 @@ def test_batch_process_documents_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.batch_process_documents), "__call__" + type(client.transport.batch_process_documents), "__call__" ) as call: call.return_value = operations_pb2.Operation(name="operations/op") @@ -803,7 +808,7 @@ async def test_batch_process_documents_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.batch_process_documents), "__call__" + type(client.transport.batch_process_documents), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/op") @@ -828,7 +833,7 @@ def test_batch_process_documents_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.batch_process_documents), "__call__" + type(client.transport.batch_process_documents), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -866,7 +871,7 @@ async def test_batch_process_documents_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.batch_process_documents), "__call__" + type(client.transport.batch_process_documents), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -913,7 +918,7 @@ def test_review_document( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.review_document), "__call__") as call: + with mock.patch.object(type(client.transport.review_document), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/spam") @@ -934,19 +939,20 @@ def test_review_document_from_dict(): @pytest.mark.asyncio -async def test_review_document_async(transport: str = "grpc_asyncio"): +async def test_review_document_async( + transport: str = "grpc_asyncio", + request_type=document_processor_service.ReviewDocumentRequest, +): client = DocumentProcessorServiceAsyncClient( credentials=credentials.AnonymousCredentials(), transport=transport, ) # Everything is optional in proto3 as far as the runtime is concerned, # and we are mocking out the actual API, so just send an empty request. - request = document_processor_service.ReviewDocumentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.review_document), "__call__" - ) as call: + with mock.patch.object(type(client.transport.review_document), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/spam") @@ -958,12 +964,17 @@ async def test_review_document_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == document_processor_service.ReviewDocumentRequest() # Establish that the response is the type that we expect. assert isinstance(response, future.Future) +@pytest.mark.asyncio +async def test_review_document_async_from_dict(): + await test_review_document_async(request_type=dict) + + def test_review_document_field_headers(): client = DocumentProcessorServiceClient( credentials=credentials.AnonymousCredentials(), @@ -975,7 +986,7 @@ def test_review_document_field_headers(): request.human_review_config = "human_review_config/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.review_document), "__call__") as call: + with mock.patch.object(type(client.transport.review_document), "__call__") as call: call.return_value = operations_pb2.Operation(name="operations/op") client.review_document(request) @@ -1005,9 +1016,7 @@ async def test_review_document_field_headers_async(): request.human_review_config = "human_review_config/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.review_document), "__call__" - ) as call: + with mock.patch.object(type(client.transport.review_document), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/op") ) @@ -1033,7 +1042,7 @@ def test_review_document_flattened(): ) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.review_document), "__call__") as call: + with mock.patch.object(type(client.transport.review_document), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -1070,9 +1079,7 @@ async def test_review_document_flattened_async(): ) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.review_document), "__call__" - ) as call: + with mock.patch.object(type(client.transport.review_document), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -1144,7 +1151,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = DocumentProcessorServiceClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -1183,7 +1190,7 @@ def test_transport_grpc_default(): credentials=credentials.AnonymousCredentials(), ) assert isinstance( - client._transport, transports.DocumentProcessorServiceGrpcTransport, + client.transport, transports.DocumentProcessorServiceGrpcTransport, ) @@ -1285,7 +1292,7 @@ def test_document_processor_service_host_no_port(): api_endpoint="us-documentai.googleapis.com" ), ) - assert client._transport._host == "us-documentai.googleapis.com:443" + assert client.transport._host == "us-documentai.googleapis.com:443" def test_document_processor_service_host_with_port(): @@ -1295,7 +1302,7 @@ def test_document_processor_service_host_with_port(): api_endpoint="us-documentai.googleapis.com:8000" ), ) - assert client._transport._host == "us-documentai.googleapis.com:8000" + assert client.transport._host == "us-documentai.googleapis.com:8000" def test_document_processor_service_grpc_transport_channel(): @@ -1307,6 +1314,7 @@ def test_document_processor_service_grpc_transport_channel(): ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None def test_document_processor_service_grpc_asyncio_transport_channel(): @@ -1318,6 +1326,7 @@ def test_document_processor_service_grpc_asyncio_transport_channel(): ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None @pytest.mark.parametrize( @@ -1365,6 +1374,7 @@ def test_document_processor_service_transport_channel_mtls_with_client_cert_sour quota_project_id=None, ) assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred @pytest.mark.parametrize( @@ -1411,7 +1421,7 @@ def test_document_processor_service_grpc_lro_client(): client = DocumentProcessorServiceClient( credentials=credentials.AnonymousCredentials(), transport="grpc", ) - transport = client._transport + transport = client.transport # Ensure that we have a api-core operations client. assert isinstance(transport.operations_client, operations_v1.OperationsClient,) @@ -1424,7 +1434,7 @@ def test_document_processor_service_grpc_lro_async_client(): client = DocumentProcessorServiceAsyncClient( credentials=credentials.AnonymousCredentials(), transport="grpc_asyncio", ) - transport = client._client._transport + transport = client.transport # Ensure that we have a api-core operations client. assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) @@ -1433,6 +1443,159 @@ def test_document_processor_service_grpc_lro_async_client(): assert transport.operations_client is transport.operations_client +def test_human_review_config_path(): + project = "squid" + location = "clam" + processor = "whelk" + + expected = "projects/{project}/locations/{location}/processors/{processor}/humanReviewConfig".format( + project=project, location=location, processor=processor, + ) + actual = DocumentProcessorServiceClient.human_review_config_path( + project, location, processor + ) + assert expected == actual + + +def test_parse_human_review_config_path(): + expected = { + "project": "octopus", + "location": "oyster", + "processor": "nudibranch", + } + path = DocumentProcessorServiceClient.human_review_config_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentProcessorServiceClient.parse_human_review_config_path(path) + assert expected == actual + + +def test_processor_path(): + project = "cuttlefish" + location = "mussel" + processor = "winkle" + + expected = "projects/{project}/locations/{location}/processors/{processor}".format( + project=project, location=location, processor=processor, + ) + actual = DocumentProcessorServiceClient.processor_path(project, location, processor) + assert expected == actual + + +def test_parse_processor_path(): + expected = { + "project": "nautilus", + "location": "scallop", + "processor": "abalone", + } + path = DocumentProcessorServiceClient.processor_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentProcessorServiceClient.parse_processor_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "squid" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = DocumentProcessorServiceClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "clam", + } + path = DocumentProcessorServiceClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentProcessorServiceClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "whelk" + + expected = "folders/{folder}".format(folder=folder,) + actual = DocumentProcessorServiceClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "octopus", + } + path = DocumentProcessorServiceClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentProcessorServiceClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "oyster" + + expected = "organizations/{organization}".format(organization=organization,) + actual = DocumentProcessorServiceClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nudibranch", + } + path = DocumentProcessorServiceClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentProcessorServiceClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "cuttlefish" + + expected = "projects/{project}".format(project=project,) + actual = DocumentProcessorServiceClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "mussel", + } + path = DocumentProcessorServiceClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentProcessorServiceClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "winkle" + location = "nautilus" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = DocumentProcessorServiceClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "scallop", + "location": "abalone", + } + path = DocumentProcessorServiceClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentProcessorServiceClient.parse_common_location_path(path) + assert expected == actual + + def test_client_withDEFAULT_CLIENT_INFO(): client_info = gapic_v1.client_info.ClientInfo()