From b091bfc523ee40af4ef0b28abfc0c26dcdf09ebe Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Tue, 25 Jan 2022 17:11:25 -0500 Subject: [PATCH] fix: preserve hyperlinks with hyphens (#1140) The default behaviour of textwrap.wrap is to break text on hyphens. This PR sets the break_on_hyphens parameter of textwrap.wrap to False in order to preserve hyperlinks with hyphens. Fixes #1131 --- gapic/utils/lines.py | 6 ++++++ .../services/iam_credentials/async_client.py | 4 ++-- .../iam/credentials_v1/services/iam_credentials/client.py | 4 ++-- .../services/iam_credentials/transports/grpc.py | 4 ++-- .../services/iam_credentials/transports/grpc_asyncio.py | 4 ++-- .../logging_v2/services/config_service_v2/async_client.py | 8 ++++---- .../cloud/logging_v2/services/config_service_v2/client.py | 8 ++++---- .../google/cloud/logging_v2/types/logging_config.py | 3 +-- .../redis/google/cloud/redis_v1/types/cloud_redis.py | 4 ++-- tests/unit/utils/test_lines.py | 4 ++++ 10 files changed, 29 insertions(+), 20 deletions(-) diff --git a/gapic/utils/lines.py b/gapic/utils/lines.py index 64d32a31fa..6da582c44a 100644 --- a/gapic/utils/lines.py +++ b/gapic/utils/lines.py @@ -78,9 +78,12 @@ def wrap(text: str, width: int, *, offset: int = None, indent: int = 0) -> str: # Break off the first line of the string to address non-zero offsets. first = text.split('\n')[0] + '\n' if len(first) > width - offset: + # Ensure `break_on_hyphens` is set to `False` when using + # `textwrap.wrap` to avoid breaking hyperlinks with hyphens. initial = textwrap.wrap(first, break_long_words=False, width=width - offset, + break_on_hyphens=False, ) # Strip the first \n from the text so it is not misidentified as an # intentionally short line below. @@ -107,11 +110,14 @@ def wrap(text: str, width: int, *, offset: int = None, indent: int = 0) -> str: # Wrap the remainder of the string at the desired width. return '{first}{text}'.format( first=first, + # Ensure `break_on_hyphens` is set to `False` when using + # `textwrap.fill` to avoid breaking hyperlinks with hyphens. text='\n'.join([textwrap.fill( break_long_words=False, initial_indent=' ' * indent, subsequent_indent=' ' * indent, text=token, width=width, + break_on_hyphens=False, ) for token in tokens]), ).rstrip('\n') diff --git a/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/async_client.py b/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/async_client.py index cd7019d14e..28b31e1bf5 100644 --- a/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/async_client.py +++ b/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/async_client.py @@ -48,8 +48,8 @@ class IAMCredentialsAsyncClient: Service account credentials are used to temporarily assume the identity of the service account. Supported credential types - include OAuth 2.0 access tokens, OpenID Connect ID tokens, self- - signed JSON Web Tokens (JWTs), and more. + include OAuth 2.0 access tokens, OpenID Connect ID tokens, + self-signed JSON Web Tokens (JWTs), and more. """ _client: IAMCredentialsClient diff --git a/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/client.py b/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/client.py index 712492ba8a..5c330ce897 100644 --- a/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/client.py +++ b/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/client.py @@ -83,8 +83,8 @@ class IAMCredentialsClient(metaclass=IAMCredentialsClientMeta): Service account credentials are used to temporarily assume the identity of the service account. Supported credential types - include OAuth 2.0 access tokens, OpenID Connect ID tokens, self- - signed JSON Web Tokens (JWTs), and more. + include OAuth 2.0 access tokens, OpenID Connect ID tokens, + self-signed JSON Web Tokens (JWTs), and more. """ @staticmethod diff --git a/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/grpc.py b/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/grpc.py index c213c2fb32..597a4480ba 100644 --- a/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/grpc.py +++ b/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/grpc.py @@ -39,8 +39,8 @@ class IAMCredentialsGrpcTransport(IAMCredentialsTransport): Service account credentials are used to temporarily assume the identity of the service account. Supported credential types - include OAuth 2.0 access tokens, OpenID Connect ID tokens, self- - signed JSON Web Tokens (JWTs), and more. + include OAuth 2.0 access tokens, OpenID Connect ID tokens, + self-signed JSON Web Tokens (JWTs), and more. This class defines the same methods as the primary client, so the primary client can load the underlying transport implementation diff --git a/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/grpc_asyncio.py b/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/grpc_asyncio.py index 8515d5fdb1..800002bfcd 100644 --- a/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/grpc_asyncio.py +++ b/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/transports/grpc_asyncio.py @@ -40,8 +40,8 @@ class IAMCredentialsGrpcAsyncIOTransport(IAMCredentialsTransport): Service account credentials are used to temporarily assume the identity of the service account. Supported credential types - include OAuth 2.0 access tokens, OpenID Connect ID tokens, self- - signed JSON Web Tokens (JWTs), and more. + include OAuth 2.0 access tokens, OpenID Connect ID tokens, + self-signed JSON Web Tokens (JWTs), and more. This class defines the same methods as the primary client, so the primary client can load the underlying transport implementation diff --git a/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/async_client.py b/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/async_client.py index 01dd8b6300..95f8ac1c8c 100644 --- a/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/async_client.py +++ b/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/async_client.py @@ -2418,8 +2418,8 @@ def sample_get_cmek_settings(): The request object. The parameters to [GetCmekSettings][google.logging.v2.ConfigServiceV2.GetCmekSettings]. See [Enabling CMEK for Logs - Router](https://cloud.google.com/logging/docs/routing/managed- - encryption) for more information. + Router](https://cloud.google.com/logging/docs/routing/managed-encryption) + for more information. retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. @@ -2520,8 +2520,8 @@ def sample_update_cmek_settings(): The request object. The parameters to [UpdateCmekSettings][google.logging.v2.ConfigServiceV2.UpdateCmekSettings]. See [Enabling CMEK for Logs - Router](https://cloud.google.com/logging/docs/routing/managed- - encryption) for more information. + Router](https://cloud.google.com/logging/docs/routing/managed-encryption) + for more information. retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. diff --git a/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/client.py b/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/client.py index e861ff0898..0f41c6dc22 100644 --- a/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/client.py +++ b/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/client.py @@ -2616,8 +2616,8 @@ def sample_get_cmek_settings(): The request object. The parameters to [GetCmekSettings][google.logging.v2.ConfigServiceV2.GetCmekSettings]. See [Enabling CMEK for Logs - Router](https://cloud.google.com/logging/docs/routing/managed- - encryption) for more information. + Router](https://cloud.google.com/logging/docs/routing/managed-encryption) + for more information. retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. @@ -2720,8 +2720,8 @@ def sample_update_cmek_settings(): The request object. The parameters to [UpdateCmekSettings][google.logging.v2.ConfigServiceV2.UpdateCmekSettings]. See [Enabling CMEK for Logs - Router](https://cloud.google.com/logging/docs/routing/managed- - encryption) for more information. + Router](https://cloud.google.com/logging/docs/routing/managed-encryption) + for more information. retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. diff --git a/tests/integration/goldens/logging/google/cloud/logging_v2/types/logging_config.py b/tests/integration/goldens/logging/google/cloud/logging_v2/types/logging_config.py index ce8b103af2..068af0b236 100644 --- a/tests/integration/goldens/logging/google/cloud/logging_v2/types/logging_config.py +++ b/tests/integration/goldens/logging/google/cloud/logging_v2/types/logging_config.py @@ -144,8 +144,7 @@ class LogView(proto.Message): name (str): The resource name of the view. For example - "projects/my-project-id/locations/my- - location/buckets/my-bucket-id/views/my-view + "projects/my-project-id/locations/my-location/buckets/my-bucket-id/views/my-view description (str): Describes this view. create_time (google.protobuf.timestamp_pb2.Timestamp): diff --git a/tests/integration/goldens/redis/google/cloud/redis_v1/types/cloud_redis.py b/tests/integration/goldens/redis/google/cloud/redis_v1/types/cloud_redis.py index 32aff22d3f..e228a37982 100644 --- a/tests/integration/goldens/redis/google/cloud/redis_v1/types/cloud_redis.py +++ b/tests/integration/goldens/redis/google/cloud/redis_v1/types/cloud_redis.py @@ -95,8 +95,8 @@ class Instance(proto.Message): addresses that are reserved for this instance. If not provided, the service will choose an unused /29 block, for example, 10.0.0.0/29 or - 192.168.0.0/29. Ranges must be unique and non- - overlapping with existing subnets in an + 192.168.0.0/29. Ranges must be unique and + non-overlapping with existing subnets in an authorized network. host (str): Output only. Hostname or IP address of the diff --git a/tests/unit/utils/test_lines.py b/tests/unit/utils/test_lines.py index 65df6f1c36..c471cb677b 100644 --- a/tests/unit/utils/test_lines.py +++ b/tests/unit/utils/test_lines.py @@ -87,3 +87,7 @@ def test_wrap_indent_short(): def test_wrap_short_line_preserved(): assert lines.wrap('foo\nbar\nbaz', width=80) == 'foo\nbar\nbaz' + + +def test_wrap_does_not_break_hyphenated_word(): + assert lines.wrap('do-not-break', width=5) == 'do-not-break'