Skip to content

DOC-5503 enable TCE overrides #1889

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions build/local_examples.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#!/usr/bin/env python3
"""
Local Examples Processor

This script processes local examples from the local_examples/ directory
and integrates them into the existing examples system.

Works like remote examples - each file contains an EXAMPLE: header
and can be any supported language.
"""

import os
import glob
import shutil
import logging
from typing import Dict, Any

from components.example import Example
from components.util import mkdir_p
from components.structured_data import load_dict, dump_dict


# File extension to language mapping
EXTENSION_TO_LANGUAGE = {
'.py': 'python',
'.js': 'node.js',
'.go': 'go',
'.cs': 'c#',
'.java': 'java',
'.php': 'php'
}

# Language to client name mapping (from config.toml clientsExamples)
LANGUAGE_TO_CLIENT = {
'python': 'Python',
'node.js': 'Node.js',
'go': 'Go',
'c#': 'C#',
'java': 'Java-Sync', # Default to sync, could be overridden
'php': 'PHP',
'redisvl': 'RedisVL'
}


def get_language_from_extension(filename: str) -> str:
"""Get language from file extension."""
_, ext = os.path.splitext(filename)
return EXTENSION_TO_LANGUAGE.get(ext.lower())


def get_client_name_from_language(language: str) -> str:
"""Get client name from language."""
return LANGUAGE_TO_CLIENT.get(language, language.title())


def get_example_id_from_file(path: str) -> str:
"""Extract example ID from the first line of a file."""
try:
with open(path, 'r') as f:
first_line = f.readline().strip()
if 'EXAMPLE:' in first_line:
return first_line.split(':')[1].strip()
except Exception as e:
logging.error(f"Error reading example ID from {path}: {e}")
return None


def process_local_examples(local_examples_dir: str = 'local_examples',
examples_dir: str = 'examples',
examples_json: str = 'data/examples.json') -> None:
"""
Process local examples and integrate them into the examples system.

Works like remote examples - each file contains an EXAMPLE: header
and can be any supported language.

Args:
local_examples_dir: Directory containing local example source files
examples_dir: Target directory for processed examples
examples_json: Path to examples.json file
"""

if not os.path.exists(local_examples_dir):
logging.info(f"Local examples directory {local_examples_dir} not found, skipping")
return

# Load existing examples data
examples_data = {}
if os.path.exists(examples_json):
examples_data = load_dict(examples_json)

# Process each file in local_examples directory
for filename in os.listdir(local_examples_dir):
source_file = os.path.join(local_examples_dir, filename)

if not os.path.isfile(source_file):
continue

# Get language from file extension
language = get_language_from_extension(filename)
if not language:
logging.warning(f"Unknown file extension for: {filename}")
continue

# Get example ID from file content
example_id = get_example_id_from_file(source_file)
if not example_id:
logging.warning(f"No EXAMPLE: header found in {filename}")
continue

logging.info(f"Processing local example: {example_id} ({language})")

# Create target directory
target_dir = os.path.join(examples_dir, example_id)
mkdir_p(target_dir)

# Initialize example data
if example_id not in examples_data:
examples_data[example_id] = {}

# Copy file to target directory with local_ prefix
base_name = os.path.splitext(filename)[0]
ext = os.path.splitext(filename)[1]
target_filename = f"local_{base_name}{ext}"
target_file = os.path.join(target_dir, target_filename)
shutil.copy2(source_file, target_file)

# Process with Example class
example = Example(language, target_file)

# Get client name
client_name = get_client_name_from_language(language)

# Create metadata
example_metadata = {
'source': source_file,
'language': language,
'target': target_file,
'highlight': example.highlight,
'hidden': example.hidden,
'named_steps': example.named_steps,
'sourceUrl': None # Local examples don't have source URLs
}

examples_data[example_id][client_name] = example_metadata
logging.info(f"Processed {client_name} example for {example_id}")

# Save updated examples data
dump_dict(examples_json, examples_data)
logging.info(f"Updated examples data saved to {examples_json}")


if __name__ == '__main__':
logging.basicConfig(level=logging.INFO,
format='%(levelname)s: %(message)s')

process_local_examples()
print("Local examples processing complete")
17 changes: 9 additions & 8 deletions build/make.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import argparse
from datetime import datetime
import logging
import sys
import tempfile

from components.component import All
from components.util import mkdir_p
from local_examples import process_local_examples


def parse_args() -> argparse.Namespace:
Expand All @@ -30,20 +32,19 @@ def parse_args() -> argparse.Namespace:
ARGS = parse_args()
mkdir_p(ARGS.tempdir)

# Configure logging BEFORE creating objects
log_level = getattr(logging, ARGS.loglevel.upper())
logging.basicConfig(
level=log_level,
format='%(message)s %(filename)s:%(lineno)d - %(funcName)s',
force=True # Force reconfiguration in case logging was already configured
)

# Load settings
ALL = All(ARGS.stack, None, ARGS.__dict__)

# Make the stack
logging.basicConfig(
level=ARGS.loglevel, format=f'{sys.argv[0]}: %(levelname)s %(asctime)s %(message)s')
print(f'Applying all configured components"{ALL._name}"')
start = datetime.now()
ALL.apply()

# Process local examples
print('Processing local examples')
process_local_examples()

total = datetime.now() - start
print(f'+OK ({total.microseconds / 1000} ms)')
13 changes: 8 additions & 5 deletions layouts/partials/tabbed-clients-example.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
{{ $lang := .Scratch.Get "lang" }}
{{ $redisCommands := .Scratch.Get "redisCommands" }}
{{ $redisCommandsLineLimit := (or (.Scratch.Get "maxLines") 100) }}
{{ $cliTabName := (or (.Scratch.Get "cli_tab_name") ">_ Redis CLI") }}
{{ $cliFooterLinkText := .Scratch.Get "cli_footer_link_text" }}
{{ $cliFooterLinkUrl := .Scratch.Get "cli_footer_link_url" }}

{{ if not (isset $.Site.Data.examples $id) }}
{{ warnf "[tabbed-clients-example] Example not found %q for %q" $id $.Page }}
Expand All @@ -12,7 +15,7 @@
{{/* Render redis-cli example from inner content if any */}}
{{ if (ne (trim $redisCommands "\n") "") }}
{{ $redisCliContent := highlight (trim $redisCommands "\n") "plaintext" (printf "linenos=false,hl_lines=1-%d" $redisCommandsLineLimit ) }}
{{ $tabs = $tabs | append (dict "title" "redis-cli" "content" $redisCliContent "limit" $redisCommandsLineLimit) }}
{{ $tabs = $tabs | append (dict "title" "redis-cli" "displayName" $cliTabName "content" $redisCliContent "limit" $redisCommandsLineLimit "customFooterLinkText" $cliFooterLinkText "customFooterLinkUrl" $cliFooterLinkUrl) }}
{{ end }}

{{ $clientExamples := index $.Site.Data.examples $id }}
Expand All @@ -21,15 +24,15 @@
{{ $clientConfig := index $.Site.Params.clientsconfig $client }}
{{ $language := index $example "language" }}
{{ $quickstartSlug := index $clientConfig "quickstartSlug" }}

{{ if and ($example) (or (eq $lang "") (strings.Contains $lang $client)) }}
{{ $examplePath := index $example "target" }}
{{ $options := printf "linenos=false" }}

{{ if and (ne $step "") (isset $example "named_steps") (isset $example.named_steps $step) }}
{{ $options = printf "%s,hl_lines=%s" $options (index $example.named_steps $step) }}
{{ else }}
{{ if isset $example "highlight" }}
{{ if and (isset $example "highlight") (index $example "highlight") }}
{{ $options = printf "%s,hl_lines=%s" $options (delimit (index $example "highlight") " ") }}
{{ end }}
{{ end }}
Expand All @@ -40,5 +43,5 @@
{{ end }}
{{ end }}

{{ $params := dict "id" (printf "%s-step%s" $id $step) "tabs" $tabs "showFooter" (eq $lang "") }}
{{ $params := dict "id" (printf "%s-step%s" $id $step) "tabs" $tabs "showFooter" true }}
{{ partial "tabs/wrapper.html" $params }}
71 changes: 47 additions & 24 deletions layouts/partials/tabs/wrapper.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
hover:text-redis-red-600 rounded rounded-mx transition duration-150 ease-in-out"
title="Open example" for="{{ $tid }}">
{{ if eq (index $tab "title") "redis-cli" }}
{{ $cliName }}
{{ or (index $tab "displayName") $cliName }}
{{ else }}
{{ index $tab "title" }}
{{ end }}
Expand Down Expand Up @@ -54,24 +54,44 @@
<div class="cli-footer flex items-center justify-between rounded-b-md bg-slate-900 mt-0 px-4 pt-0 mb-0 transition-opacity ease-in-out duration-300 opacity-0 invisible
group-hover:opacity-100 group-hover:visible">
{{ if eq (index $tab "title") "redis-cli" }}
<div class="flex-1 text-xs text-white overflow-ellipsis">{{ $footerText }}</div>
<div class="text-right">
<a href="{{ $btnRIUrl }}" tabindex="1" class="rounded rounded-mx px-2 py-1 flex items-center text-white text-xs
hover:text-white hover:bg-slate-600 hover:border-transparent focus:outline-none
focus:ring-2 focus:white focus:border-slate-500" title="{{$btnRIText}}">
<svg class="w-4 h-4 mr-1" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.26236 5.66895L1.21732 6.07172L7.00018 8.65693V7.79842L2.26236 5.66895Z" fill="#fca5a5"/>
<path d="M2.26236 8.02271L1.21732 8.42548L7.00018 11.0119V10.1516L2.26236 8.02271Z" fill="#fca5a5"/>
<path d="M1.21732 3.7175L7.00018 6.30392V2.87805L8.66273 2.13423L7.00018 1.49512L1.21732 3.7175Z" fill="#fca5a5"/>
<path d="M7.00018 2.8781V6.30366L1.21732 3.71724V5.20004L7.00018 7.79705V8.65526L1.21732 6.07217V7.55496L7.00018 10.1553V11.0135L1.21732 8.42376V9.90656H1.18878L7.00018 12.5051L8.66273 11.7613V2.13428L7.00018 2.8781Z" fill="#f87171"/>
<path d="M9.07336 11.5777L10.7359 10.8338V4.01538L9.07336 4.7592V11.5777Z" fill="#f87171"/>
<path d="M9.07336 4.75867L10.7359 4.01485L9.07336 3.37573V4.75867Z" fill="#fca5a5"/>
<path d="M11.1481 10.6497L12.8112 9.90591V5.896L11.1487 6.63982L11.1481 10.6497Z" fill="#f87171"/>
<path d="M11.1481 6.63954L12.8112 5.89572L11.1481 5.25781V6.63954Z" fill="#fca5a5"/>
</svg>
<span>{{$btnRIText}}</span>
</a>
</div>
{{ $displayName := index $tab "displayName" }}
{{ $hasCustomTabName := and $displayName (ne $displayName $cliName) }}
{{ $customLinkText := index $tab "customFooterLinkText" }}
{{ $customLinkUrl := index $tab "customFooterLinkUrl" }}

{{ if and $customLinkText $customLinkUrl }}
<div class="text-left">
<a href="{{ $customLinkUrl }}" tabindex="1" class="rounded rounded-mx px-2 py-1 flex items-center text-white text-xs
hover:text-white hover:bg-slate-600 hover:border-transparent focus:outline-none
focus:ring-2 focus:white focus:border-slate-500" title="{{ $customLinkText }}">
<span>{{ $customLinkText }}</span>
</a>
</div>
<div class="flex-1"></div>
{{ else }}
{{ if $hasCustomTabName }}
<div class="flex-1"></div>
{{ else }}
<div class="flex-1 text-xs text-white overflow-ellipsis">{{ $footerText }}</div>
{{ end }}
<div class="text-right">
<a href="{{ $btnRIUrl }}" tabindex="1" class="rounded rounded-mx px-2 py-1 flex items-center text-white text-xs
hover:text-white hover:bg-slate-600 hover:border-transparent focus:outline-none
focus:ring-2 focus:white focus:border-slate-500" title="{{$btnRIText}}">
<svg class="w-4 h-4 mr-1" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.26236 5.66895L1.21732 6.07172L7.00018 8.65693V7.79842L2.26236 5.66895Z" fill="#fca5a5"/>
<path d="M2.26236 8.02271L1.21732 8.42548L7.00018 11.0119V10.1516L2.26236 8.02271Z" fill="#fca5a5"/>
<path d="M1.21732 3.7175L7.00018 6.30392V2.87805L8.66273 2.13423L7.00018 1.49512L1.21732 3.7175Z" fill="#fca5a5"/>
<path d="M7.00018 2.8781V6.30366L1.21732 3.71724V5.20004L7.00018 7.79705V8.65526L1.21732 6.07217V7.55496L7.00018 10.1553V11.0135L1.21732 8.42376V9.90656H1.18878L7.00018 12.5051L8.66273 11.7613V2.13428L7.00018 2.8781Z" fill="#f87171"/>
<path d="M9.07336 11.5777L10.7359 10.8338V4.01538L9.07336 4.7592V11.5777Z" fill="#f87171"/>
<path d="M9.07336 4.75867L10.7359 4.01485L9.07336 3.37573V4.75867Z" fill="#fca5a5"/>
<path d="M11.1481 10.6497L12.8112 9.90591V5.896L11.1487 6.63982L11.1481 10.6497Z" fill="#f87171"/>
<path d="M11.1481 6.63954L12.8112 5.89572L11.1481 5.25781V6.63954Z" fill="#fca5a5"/>
</svg>
<span>{{$btnRIText}}</span>
</a>
</div>
{{ end }}
{{ else }}
<!--<a href='/docs/latest/develop/clients/{{ index $tab "quickstartSlug" }}/' tabindex="1" class="rounded rounded-mx px-3 py-1 text-white text-xs-->
<a href='{{ absURL (print "develop/clients/" (index $tab "quickstartSlug")) }}/' tabindex="1" class="rounded rounded-mx px-3 py-1 text-white text-xs
Expand All @@ -81,11 +101,14 @@
</a>
<div class="w-1/2"></div>
<div class="flex-1 text-right">
<a href='{{ index $tab "sourceUrl" }}' tabindex="1" class="button text-neutral-400 hover:text-slate-100 h-6 w-6 p-1" title="Improve this code example">
<svg width="18" height="16" viewBox="0 0 18 16" fill="github" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.99953 1.43397e-06C7.09918 -0.000897921 5.26058 0.674713 3.81295 1.90585C2.36533 3.13699 1.40324 4.84324 1.09896 6.71907C0.794684 8.5949 1.1681 10.5178 2.15233 12.1434C3.13657 13.769 4.66734 14.9912 6.47053 15.591C6.87053 15.664 7.01653 15.417 7.01653 15.205C7.01653 15.015 7.00953 14.512 7.00553 13.845C4.78053 14.328 4.31053 12.772 4.31053 12.772C4.16325 12.2887 3.84837 11.8739 3.42253 11.602C2.69653 11.102 3.47753 11.116 3.47753 11.116C3.73043 11.1515 3.97191 11.2442 4.18365 11.3869C4.39539 11.5297 4.57182 11.7189 4.69953 11.94C4.80755 12.1377 4.95378 12.3119 5.12972 12.4526C5.30567 12.5933 5.50782 12.6976 5.72442 12.7595C5.94103 12.8214 6.16778 12.8396 6.39148 12.813C6.61519 12.7865 6.83139 12.7158 7.02753 12.605C7.06381 12.1992 7.24399 11.8197 7.53553 11.535C5.75953 11.335 3.89153 10.647 3.89153 7.581C3.88005 6.78603 4.17513 6.01716 4.71553 5.434C4.47318 4.74369 4.50322 3.98693 4.79953 3.318C4.79953 3.318 5.47053 3.103 6.99953 4.138C8.31074 3.77905 9.69432 3.77905 11.0055 4.138C12.5325 3.103 13.2055 3.318 13.2055 3.318C13.5012 3.9877 13.5294 4.74513 13.2845 5.435C13.8221 6.01928 14.114 6.78817 14.0995 7.582C14.0995 10.655 12.2285 11.332 10.4465 11.53C10.6375 11.724 10.7847 11.9566 10.8784 12.2123C10.972 12.4679 11.0099 12.7405 10.9895 13.012C10.9895 14.081 10.9795 14.944 10.9795 15.206C10.9795 15.42 11.1235 15.669 11.5295 15.591C13.3328 14.9911 14.8636 13.7689 15.8479 12.1432C16.8321 10.5175 17.2054 8.59447 16.901 6.71858C16.5966 4.84268 15.6343 3.13642 14.1865 1.90536C12.7387 0.674306 10.9 -0.0011359 8.99953 1.43397e-06Z" fill="white"/>
</svg>
</a>
{{ $sourceUrl := index $tab "sourceUrl" }}
{{ if $sourceUrl }}
<a href='{{ $sourceUrl }}' tabindex="1" class="button text-neutral-400 hover:text-slate-100 h-6 w-6 p-1" title="Improve this code example">
<svg width="18" height="16" viewBox="0 0 18 16" fill="github" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.99953 1.43397e-06C7.09918 -0.000897921 5.26058 0.674713 3.81295 1.90585C2.36533 3.13699 1.40324 4.84324 1.09896 6.71907C0.794684 8.5949 1.1681 10.5178 2.15233 12.1434C3.13657 13.769 4.66734 14.9912 6.47053 15.591C6.87053 15.664 7.01653 15.417 7.01653 15.205C7.01653 15.015 7.00953 14.512 7.00553 13.845C4.78053 14.328 4.31053 12.772 4.31053 12.772C4.16325 12.2887 3.84837 11.8739 3.42253 11.602C2.69653 11.102 3.47753 11.116 3.47753 11.116C3.73043 11.1515 3.97191 11.2442 4.18365 11.3869C4.39539 11.5297 4.57182 11.7189 4.69953 11.94C4.80755 12.1377 4.95378 12.3119 5.12972 12.4526C5.30567 12.5933 5.50782 12.6976 5.72442 12.7595C5.94103 12.8214 6.16778 12.8396 6.39148 12.813C6.61519 12.7865 6.83139 12.7158 7.02753 12.605C7.06381 12.1992 7.24399 11.8197 7.53553 11.535C5.75953 11.335 3.89153 10.647 3.89153 7.581C3.88005 6.78603 4.17513 6.01716 4.71553 5.434C4.47318 4.74369 4.50322 3.98693 4.79953 3.318C4.79953 3.318 5.47053 3.103 6.99953 4.138C8.31074 3.77905 9.69432 3.77905 11.0055 4.138C12.5325 3.103 13.2055 3.318 13.2055 3.318C13.5012 3.9877 13.5294 4.74513 13.2845 5.435C13.8221 6.01928 14.114 6.78817 14.0995 7.582C14.0995 10.655 12.2285 11.332 10.4465 11.53C10.6375 11.724 10.7847 11.9566 10.8784 12.2123C10.972 12.4679 11.0099 12.7405 10.9895 13.012C10.9895 14.081 10.9795 14.944 10.9795 15.206C10.9795 15.42 11.1235 15.669 11.5295 15.591C13.3328 14.9911 14.8636 13.7689 15.8479 12.1432C16.8321 10.5175 17.2054 8.59447 16.901 6.71858C16.5966 4.84268 15.6343 3.13642 14.1865 1.90536C12.7387 0.674306 10.9 -0.0011359 8.99953 1.43397e-06Z" fill="white"/>
</svg>
</a>
{{ end }}
</div>
{{ end }}
</div>
Expand Down
3 changes: 3 additions & 0 deletions layouts/shortcodes/clients-example.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
{{ .Scratch.Set "step" (.Get 1) }}
{{ .Scratch.Set "lang" (.Get 2) }}
{{ .Scratch.Set "maxLines" (.Get 3) }}
{{ .Scratch.Set "cli_tab_name" (.Get 4) }}
{{ .Scratch.Set "cli_footer_link_text" (.Get 5) }}
{{ .Scratch.Set "cli_footer_link_url" (.Get 6) }}
{{ .Scratch.Set "redisCommands" .Inner }}
{{ partial "tabbed-clients-example.html" . }}